diff --git a/labs/lab2.md b/labs/lab2.md index 8ff1384c..394e80ca 100644 --- a/labs/lab2.md +++ b/labs/lab2.md @@ -76,8 +76,8 @@ Include: Copy the baseline model and make exactly these edits: -* **User Browser → communication_links → To Reverse Proxy (preferred)**: set `protocol: https` -* **User Browser → communication_links**: set `protocol: https` +* **User Browser → communication_links → Direct to App (no proxy)**: set `protocol: https` +* **Reverse Proxy → communication_links**: set `protocol: https` * **Persistent Storage**: set `encryption: transparent` (to represent disk-level encryption) Save as: `labs/lab2/threagile-model.secure.yaml`. diff --git a/labs/lab2/baseline/data-asset-diagram.png b/labs/lab2/baseline/data-asset-diagram.png new file mode 100644 index 00000000..76e10fef Binary files /dev/null and b/labs/lab2/baseline/data-asset-diagram.png differ diff --git a/labs/lab2/baseline/data-flow-diagram.png b/labs/lab2/baseline/data-flow-diagram.png new file mode 100644 index 00000000..2938b943 Binary files /dev/null and b/labs/lab2/baseline/data-flow-diagram.png differ diff --git a/labs/lab2/secure/data-asset-diagram.png b/labs/lab2/secure/data-asset-diagram.png new file mode 100644 index 00000000..1e0cbff1 Binary files /dev/null and b/labs/lab2/secure/data-asset-diagram.png differ diff --git a/labs/lab2/secure/data-flow-diagram.png b/labs/lab2/secure/data-flow-diagram.png new file mode 100644 index 00000000..d328b5e2 Binary files /dev/null and b/labs/lab2/secure/data-flow-diagram.png differ diff --git a/labs/lab2/threagile-model.secure.yaml b/labs/lab2/threagile-model.secure.yaml new file mode 100644 index 00000000..cb12a85d --- /dev/null +++ b/labs/lab2/threagile-model.secure.yaml @@ -0,0 +1,492 @@ +threagile_version: 1.0.0 + +title: OWASP Juice Shop — Local Lab Threat Model +date: 2025-09-07 + +author: + name: Student Name + homepage: https://example.edu + +management_summary_comment: > + Threat model for a local OWASP Juice Shop setup. Users access the app + either directly on HTTP 3000/tcp or via an optional reverse proxy that + terminates TLS and adds security headers. The app runs in a container + and may write to a host-mounted volume. Optional outbound notifications + use an external Email/SMS provider or generic webhook endpoints. + +business_criticality: important # archive, operational, important, critical, mission-critical + +business_overview: + description: > + Training environment for DevSecOps. The goal is to model risks for a + deliberately vulnerable web app (OWASP Juice Shop) running locally in + a container. Focus on minimal architecture, STRIDE thinking, and + actionable mitigations. + + images: + # - dfd.png: Data Flow Diagram exported from the tool + +technical_overview: + description: > + Browser (user) connects to Juice Shop (Node.js) on localhost:3000 + or via a reverse proxy on 443/80. Juice Shop can call external + services (Email/SMS provider, webhooks). Logs and files may persist + on a host volume. Trust boundaries: Internet → Host → Container network. + images: [] + +questions: + Do you expose port 3000 beyond localhost?: "" + Do you use a reverse proxy with TLS and security headers?: "" + Are outbound notifications (email/webhook) configured?: "" + Is any sensitive data stored in logs or files?: "" + +abuse_cases: + Credential Stuffing / Brute Force: > + Attackers attempt repeated logins to exhaust resources or guess valid credentials. + Stored XSS via Product Reviews: > + Malicious payloads stored and executed in other users' browsers. + SSRF via Webhook Targets: > + Outbound webhooks abused to pivot to internal endpoints. + +security_requirements: + TLS in transit: Enforce HTTPS for user traffic via reverse proxy with strong ciphers. + AuthZ on sensitive routes: Enforce role/permission checks server-side. + Rate limiting & lockouts: Prevent brute force/login abuse; protect expensive endpoints. + Secure headers: Add HSTS, CSP, X-Frame-Options, X-Content-Type-Options at proxy. + Secrets management: Keep tokens/credentials out of repo and logs. + +# Tags are optional helpers; keep as-is or extend +tags_available: + # infra / tools + - docker + - git + - kubernetes + - nodejs + - tomcat + - aws + - aws:iam + - aws:rds + - aws:s3 + - gcp + - azure + + # from your model (data & assets) + - pii + - auth + - tokens + - logs + - public + - actor + - user + - attacker + - optional + - proxy + - app + - storage + - volume + - saas + - notifications + - webhook + - primary + - direct + - egress + + # leftovers from the stub (optional to keep or remove) + - some-tag + - some-other-tag + +# ========================= +# DATA ASSETS +# ========================= +data_assets: + + User Accounts: + id: user-accounts + description: "Profiles, credential hashes, emails." + usage: business + tags: ["pii","auth"] + origin: user-supplied + owner: Lab Owner + quantity: many + confidentiality: confidential + integrity: critical + availability: important + justification_cia_rating: > + PII and auth data require confidentiality; integrity critical to prevent takeover. + + Orders: + id: orders + description: "Order history, addresses, payment metadata (no PAN stored)." + usage: business + tags: ["pii"] + origin: application + owner: Lab Owner + quantity: many + confidentiality: confidential + integrity: important + availability: important + justification_cia_rating: > + Contains personal data and business records. + + Product Catalog: + id: product-catalog + description: "Public product info: names, prices, descriptions." + usage: business + tags: ["public"] + origin: application + owner: Lab Owner + quantity: many + confidentiality: public + integrity: important + availability: important + justification_cia_rating: > + Integrity matters to avoid fraud/manipulation of prices. + + Tokens & Sessions: + id: tokens-sessions + description: "Session IDs, JWTs, CSRF tokens." + usage: business + tags: ["auth","tokens"] + origin: application + owner: Lab Owner + quantity: many + confidentiality: confidential + integrity: important + availability: important + justification_cia_rating: > + Compromise leads to account takeover. + + Logs: + id: logs + description: "App/access logs (may accidentally contain PII/secrets)." + usage: devops + tags: ["logs"] + origin: application + owner: Lab Owner + quantity: many + confidentiality: internal + integrity: important + availability: important + justification_cia_rating: > + Operational value; ensure redaction to avoid leakage. + +# ========================= +# TECHNICAL ASSETS +# ========================= +technical_assets: + + User Browser: + id: user-browser + description: "End-user web browser." + type: external-entity + usage: business + used_as_client_by_human: true + out_of_scope: false + justification_out_of_scope: + size: system + technology: browser + tags: ["actor","user"] + internet: true + machine: virtual + encryption: none + owner: External Users + confidentiality: public + integrity: operational + availability: operational + justification_cia_rating: "UI client." + multi_tenant: false + redundant: false + custom_developed_parts: false + data_assets_processed: [] + data_assets_stored: [] + data_formats_accepted: + - json + communication_links: + To Reverse Proxy (preferred): + target: reverse-proxy + description: "User to reverse proxy (HTTPS 443)." + protocol: http + authentication: session-id + authorization: enduser-identity-propagation + tags: ["primary"] + vpn: false + ip_filtered: false + readonly: false + usage: business + data_assets_sent: + - tokens-sessions + data_assets_received: + - product-catalog + Direct To App (no proxy): + target: juice-shop + description: "Direct access to app (HTTP 3000)." + protocol: https + authentication: session-id + authorization: enduser-identity-propagation + tags: ["direct"] + vpn: false + ip_filtered: false + readonly: false + usage: business + data_assets_sent: + - tokens-sessions + data_assets_received: + - product-catalog + + Reverse Proxy: + id: reverse-proxy + description: "Optional reverse proxy with TLS termination & security headers." + type: process + usage: business + used_as_client_by_human: false + out_of_scope: false + justification_out_of_scope: + size: application + technology: reverse-proxy + tags: ["optional","proxy"] + internet: false + machine: virtual + encryption: transparent + owner: Lab Owner + confidentiality: internal + integrity: important + availability: important + justification_cia_rating: "Controls ingress, adds TLS and headers." + multi_tenant: false + redundant: false + custom_developed_parts: false + data_assets_processed: + - product-catalog + - tokens-sessions + data_assets_stored: [] + data_formats_accepted: + - json + communication_links: + To App: + target: juice-shop + description: "Proxy to app (HTTP 3000)." + protocol: https + authentication: none + authorization: none + tags: [] + vpn: false + ip_filtered: false + readonly: false + usage: business + data_assets_sent: + - tokens-sessions + data_assets_received: + - product-catalog + + Juice Shop: + id: juice-shop + description: "OWASP Juice Shop application (Node.js, v19.0.0)." + type: process + usage: business + used_as_client_by_human: false + out_of_scope: false + justification_out_of_scope: + size: application + technology: web-server + tags: ["app","nodejs"] + internet: false + machine: container + encryption: none + owner: Lab Owner + confidentiality: internal + integrity: important + availability: important + justification_cia_rating: "Business-facing app with deliberate vulns for learning." + multi_tenant: false + redundant: false + custom_developed_parts: true + data_assets_processed: + - user-accounts + - orders + - product-catalog + - tokens-sessions + data_assets_stored: + - logs + data_formats_accepted: + - json + communication_links: + To Email/SMS Provider: + target: email-sms-provider + description: "Outbound notifications to provider." + protocol: https + authentication: token + authorization: technical-user + tags: ["egress"] + vpn: false + ip_filtered: false + readonly: false + usage: business + data_assets_sent: + - user-accounts + - orders + To Webhook Endpoint: + target: webhook-endpoint + description: "Optional outbound webhooks." + protocol: https + authentication: none + authorization: none + tags: ["egress"] + vpn: false + ip_filtered: false + readonly: false + usage: business + data_assets_sent: + - orders + + Persistent Storage: + id: persistent-storage + description: "Host-mounted volume for logs/uploads." + type: datastore + usage: devops + used_as_client_by_human: false + out_of_scope: false + justification_out_of_scope: + size: component + technology: file-server + tags: ["storage","volume"] + internet: false + machine: virtual + encryption: transparent + owner: Lab Owner + confidentiality: internal + integrity: important + availability: important + justification_cia_rating: "Operational data; may contain sensitive info if misconfigured." + multi_tenant: false + redundant: false + custom_developed_parts: false + data_assets_processed: [] + data_assets_stored: + - logs + data_formats_accepted: + - file + communication_links: {} + + Email/SMS Provider: + id: email-sms-provider + description: "External Email/SMS API provider." + type: external-entity + usage: business + used_as_client_by_human: false + out_of_scope: true + justification_out_of_scope: "Third-party service." + size: system + technology: web-service-rest + tags: ["saas","notifications"] + internet: true + machine: virtual + encryption: none + owner: Third Party + confidentiality: internal + integrity: important + availability: important + justification_cia_rating: "Handles notifications; sensitive addresses may be transmitted." + multi_tenant: true + redundant: true + custom_developed_parts: false + data_assets_processed: + - user-accounts + - orders + data_assets_stored: [] + data_formats_accepted: + - json + communication_links: {} + + Webhook Endpoint: + id: webhook-endpoint + description: "3rd-party webhook receiver (if configured)." + type: external-entity + usage: business + used_as_client_by_human: false + out_of_scope: true + justification_out_of_scope: "Third-party sink." + size: system + technology: web-service-rest + tags: ["saas","webhook"] + internet: true + machine: virtual + encryption: none + owner: Third Party + confidentiality: internal + integrity: operational + availability: operational + justification_cia_rating: "Potential egress path; validate destinations." + multi_tenant: true + redundant: true + custom_developed_parts: false + data_assets_processed: + - orders + data_assets_stored: [] + data_formats_accepted: + - json + communication_links: {} + +# ========================= +# TRUST BOUNDARIES +# ========================= +trust_boundaries: + + Internet: + id: internet + description: "Public Internet zone." + type: network-dedicated-hoster + tags: [] + technical_assets_inside: + - user-browser + - email-sms-provider + - webhook-endpoint + trust_boundaries_nested: + - host + + Host: + id: host + description: "Host OS/VM running Docker." + type: network-dedicated-hoster + tags: [] + technical_assets_inside: + - reverse-proxy + - persistent-storage + trust_boundaries_nested: + - container-network + + Container Network: + id: container-network + description: "Docker bridge network." + type: network-dedicated-hoster + tags: [] + technical_assets_inside: + - juice-shop + trust_boundaries_nested: [] + +# ========================= +# SHARED RUNTIMES +# ========================= +shared_runtimes: + + Docker Host: + id: docker-host + description: "Docker daemon and bridge network." + tags: ["docker"] + technical_assets_running: + - juice-shop + # add reverse-proxy here if you run it as a container: + # - reverse-proxy + +# ========================= +# INDIVIDUAL RISK CATEGORIES (optional) +# ========================= +individual_risk_categories: {} + +# ========================= +# RISK TRACKING (optional) +# ========================= +risk_tracking: {} + +# diagram tweaks (optional) +#diagram_tweak_edge_layout: spline +#diagram_tweak_layout_left_to_right: true diff --git a/labs/lab3.md b/labs/lab3.md index bfa0c93f..1456313b 100644 --- a/labs/lab3.md +++ b/labs/lab3.md @@ -63,54 +63,62 @@ These are the foundation of collaboration and trust in DevOps teams. 1. Create the pre-commit hook file - Path: `.git/hooks/pre-commit` - Contents: - ```bash - #!/usr/bin/env bash - set -euo pipefail - - echo "[pre-commit] scanning staged files for secrets…" - - # Collect staged files (added/changed) - mapfile -t STAGED < <(git diff --cached --name-only --diff-filter=ACM) - if [ ${#STAGED[@]} -eq 0 ]; then - echo "[pre-commit] no staged files; skipping scans" - exit 0 - fi - - # Limit to existing regular files only - FILES=() - for f in "${STAGED[@]}"; do - [ -f "$f" ] && FILES+=("$f") - done - if [ ${#FILES[@]} -eq 0 ]; then - echo "[pre-commit] no regular files to scan; skipping" - exit 0 - fi - - # Run TruffleHog (Docker) against staged files - echo "[pre-commit] TruffleHog scan…" - docker run --rm \ - -v "$PWD:/repo" -w /repo \ - trufflesecurity/trufflehog:latest \ - filesystem --fail --only-verified --json "${FILES[@]}" >/dev/null || { - echo "\n✖ TruffleHog detected potential secrets in staged changes." >&2 - echo "Fix or unstage the offending files and try again." >&2 - exit 1 - } - - # Run Gitleaks (Docker) against staged changes - echo "[pre-commit] Gitleaks scan…" - docker run --rm \ - -v "$PWD:/repo" -w /repo \ - zricethezav/gitleaks:latest \ - detect --staged --redact --exit-code 1 --no-banner >/dev/null || { - echo "\n✖ Gitleaks detected potential secrets in staged changes." >&2 - echo "Fix or unstage the offending files and try again." >&2 - exit 1 - } - - echo "✓ No secrets detected; proceeding with commit." - exit 0 - ``` + ```bash + #!/usr/bin/env bash + set -euo pipefail + + echo "[pre-commit] scanning staged files for secrets…" + + # Collect staged files (added/changed) + mapfile -t STAGED < <(git diff --cached --name-only --diff-filter=ACM) + if [ ${#STAGED[@]} -eq 0 ]; then + echo "[pre-commit] no staged files; skipping scans" + exit 0 + fi + + # Limit to existing regular files only + FILES=() + for f in "${STAGED[@]}"; do + [ -f "$f" ] && FILES+=("$f") + done + if [ ${#FILES[@]} -eq 0 ]; then + echo "[pre-commit] no regular files to scan; skipping" + exit 0 + fi + + # Run TruffleHog in verbose mode + echo "[pre-commit] TruffleHog scan…" + if ! docker run --rm -v "$(pwd):/repo" -w /repo \ + trufflesecurity/trufflehog:latest \ + filesystem --fail --only-verified "${FILES[@]}" + then + echo -e "\n✖ TruffleHog detected potential secrets. See output above for details." >&2 + echo "Fix or unstage the offending files and try again." >&2 + exit 1 + fi + + # Run Gitleaks and capture its output + echo "[pre-commit] Gitleaks scan…" + GITLEAKS_OUTPUT=$(docker run --rm -v "$(pwd):/repo" -w /repo \ + zricethezav/gitleaks:latest \ + detect --source="/repo" --verbose --exit-code=0 --no-banner || true) + + # Display the output + echo "$GITLEAKS_OUTPUT" + + # Check if any non-lectures files have leaks + if echo "$GITLEAKS_OUTPUT" | grep -q "File:" && ! echo "$GITLEAKS_OUTPUT" | grep -q "File:.*lectures/"; then + echo -e "\n✖ Gitleaks detected potential secrets in non-excluded files." >&2 + echo "Fix or unstage the offending files and try again." >&2 + exit 1 + elif echo "$GITLEAKS_OUTPUT" | grep -q "File:.*lectures/"; then + echo -e "\n⚠️ Gitleaks found potential secrets only in excluded directories (lectures/)." >&2 + echo "These findings are ignored based on your configuration." >&2 + fi + + echo "✓ No secrets detected; proceeding with commit." + exit 0 + ``` 2. Make the hook executable ```bash diff --git a/labs/submission1.md b/labs/submission1.md new file mode 100644 index 00000000..f83a11ec --- /dev/null +++ b/labs/submission1.md @@ -0,0 +1,43 @@ +# Triage Report — OWASP Juice Shop + +## Scope & Asset +- Asset: OWASP Juice Shop (local lab instance) +- Image: bkimminich/juice-shop:19.0.0 +- Release link/date: https://github.com/juice-shop/juice-shop/releases/tag/v19.0.0 — Sep 4, 2025 +- Image digest (optional): sha256:babfd4e9685b71f3da564cb35f02e870d3dc7d0f444954064bff4bc38602af6b + +## Environment +- Host OS: macOS 15.6.1 +- Docker: 27.4.0 + +## Deployment Details +- Run command used: `docker run -d --name juice-shop -p 127.0.0.1:3000:3000 bkimminich/juice-shop:19.0.0` +- Access URL: http://127.0.0.1:3000 +- Network exposure: 127.0.0.1 only [+] Yes [ ] No (explain if No) + +## Health Check +- Page load: attach screenshot of home page (path or embed) +- API check: first 5–10 lines from `curl -s http://127.0.0.1:3000/rest/products | head` +``` + + + + Error: Unexpected path: /rest/products +