From c01d92af06aeee556e3f2e3715f1c97014325c3b Mon Sep 17 00:00:00 2001 From: diffouo44 Date: Wed, 10 Jun 2026 05:12:46 +0300 Subject: [PATCH 01/10] feat(lab1): add smoke-test workflow and auth model outputs --- .github/PULL_REQUEST_TEMPLATE.md | 34 +++ .github/workflows/lab1-smoke.yml | 39 +++ labs/lab2/threagile-model-auth.yaml | 224 ++++++++++++++ labs/lab2/threagile-model-secure.yaml | 422 ++++++++++++++++++++++++++ labs/submissions/lab1.md | 0 labs/submissions/lab2.md | 0 submissions/lab1.md | 152 ++++++++++ submissions/lab2.md | 103 +++++++ 8 files changed, 974 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/lab1-smoke.yml create mode 100644 labs/lab2/threagile-model-auth.yaml create mode 100644 labs/lab2/threagile-model-secure.yaml create mode 100644 labs/submissions/lab1.md create mode 100644 labs/submissions/lab2.md create mode 100644 submissions/lab1.md create mode 100644 submissions/lab2.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..c0c2f6bc8 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,34 @@ +# Goal + + + +# Changes + + +- +- + +# Testing + + + +```bash +# commands +``` + +```text +# observed output +``` + +# Artifacts & Screenshots + + +- [submissions/labN.md](../blob/HEAD/submissions/lab1.md) + +--- + +## Checklist + +- [ ] Title is clear (`feat(labN): ` style) +- [ ] No secrets/large temp files committed +- [ ] Submission file at `submissions/labN.md` exists diff --git a/.github/workflows/lab1-smoke.yml b/.github/workflows/lab1-smoke.yml new file mode 100644 index 000000000..fc6348941 --- /dev/null +++ b/.github/workflows/lab1-smoke.yml @@ -0,0 +1,39 @@ +name: Lab1 Smoke Test + +on: + pull_request: + branches: [ main ] + +permissions: + contents: read + +jobs: + smoke: + name: Smoke-test Juice Shop + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run Juice Shop container + run: | + docker run -d --name juice-shop -p 3000:3000 bkimminich/juice-shop:v20.0.0 + + - name: Wait for Juice Shop to respond + run: | + for i in $(seq 1 30); do + if curl --silent --fail http://localhost:3000/rest/admin/application-version >/dev/null 2>&1; then + echo "juice-shop healthy"; exit 0 + fi + sleep 2 + done + echo "juice-shop did not become healthy"; docker logs juice-shop || true; exit 1 + + - name: Curl homepage + run: | + curl --silent --fail -I http://localhost:3000 | head -n 20 + + - name: Stop container + if: always() + run: | + docker stop juice-shop || true diff --git a/labs/lab2/threagile-model-auth.yaml b/labs/lab2/threagile-model-auth.yaml new file mode 100644 index 000000000..2e34f764f --- /dev/null +++ b/labs/lab2/threagile-model-auth.yaml @@ -0,0 +1,224 @@ +threagile_version: 1.0.0 + +title: Juice Shop — Auth Flow Model +date: 2026-06-10 + +author: + name: Student Name + +management_summary_comment: > + Focused threat model for the authentication flow: login -> JWT issuance -> protected API. + +business_criticality: important + +tags_available: + - auth + - tokens + - pii + - actor + - user + - app + - public + - direct + - storage + +data_assets: + Credentials: + id: credentials + description: "Username and password provided by user." + usage: business + confidentiality: confidential + integrity: important + availability: important + quantity: many + + JWT Token: + id: jwt-token + description: "Signed JWT used for authentication." + usage: business + confidentiality: confidential + integrity: critical + availability: important + quantity: many + + User Session: + id: user-session + description: "Server-side session state or session identifiers." + usage: business + confidentiality: confidential + integrity: important + availability: important + quantity: many + +technical_assets: + User Browser: + id: user-browser + type: external-entity + usage: business + used_as_client_by_human: true + out_of_scope: false + size: system + technology: browser + tags: ["actor", "user"] + internet: true + machine: virtual + encryption: none + owner: External User + confidentiality: public + integrity: operational + availability: operational + justification_cia_rating: "Client controlled by end user (potentially an attacker)." + communication_links: + To Auth API: + target: auth-api + description: "Browser to Auth API (login/register)." + protocol: https + usage: business + authentication: session-id + authorization: enduser-identity-propagation + data_assets_sent: + - credentials + data_assets_received: + - jwt-token + + Auth API: + id: auth-api + type: process + usage: business + used_as_client_by_human: false + out_of_scope: false + size: application + technology: web-service-rest + tags: ["app"] + internet: false + machine: container + encryption: none + owner: Lab Owner + confidentiality: internal + integrity: important + availability: important + justification_cia_rating: "Application service handling authentication requests." + data_assets_processed: + - credentials + - jwt-token + communication_links: + To Token Signer: + target: token-signer + description: "Request signing of JWT." + protocol: https + usage: business + authentication: none + authorization: none + data_assets_sent: + - jwt-token + To User DB: + target: user-db + description: "Validate credentials; uses prepared statements." + protocol: jdbc-encrypted + usage: business + authentication: none + authorization: none + data_assets_sent: + - credentials + + Token Signer: + id: token-signer + type: process + usage: business + used_as_client_by_human: false + out_of_scope: false + size: component + technology: web-service-rest + tags: ["auth"] + internet: false + machine: container + encryption: none + owner: Lab Owner + confidentiality: internal + integrity: critical + availability: important + justification_cia_rating: "Signs tokens used for authentication." + data_assets_processed: + - jwt-token + communication_links: {} + + User DB: + id: user-db + type: datastore + usage: business + used_as_client_by_human: false + out_of_scope: false + size: system + technology: file-server + tags: ["storage"] + internet: false + machine: virtual + encryption: data-with-symmetric-shared-key + owner: Lab Owner + confidentiality: internal + integrity: important + availability: important + justification_cia_rating: "Stores credential hashes and session state; encrypted at rest." + data_assets_stored: + - credentials + - user-session + communication_links: {} + + Admin Endpoint: + id: admin-endpoint + type: process + usage: business + used_as_client_by_human: false + out_of_scope: false + size: application + technology: web-service-rest + tags: ["app"] + internet: false + machine: container + encryption: none + owner: Lab Owner + confidentiality: internal + integrity: important + availability: important + justification_cia_rating: "Admin API requiring elevated permissions." + communication_links: + From Browser: + target: admin-endpoint + description: "Protected admin API requiring JWT with admin role." + protocol: https + usage: business + authentication: session-id + authorization: enduser-identity-propagation + data_assets_received: + - jwt-token + +trust_boundaries: + Internet: + id: internet + description: "Untrusted public network (Internet)." + type: network-dedicated-hoster + technical_assets_inside: + - user-browser + trust_boundaries_nested: + - host + + Host: + id: host + description: "Local host machine / VM running the Docker environment." + type: network-dedicated-hoster + tags: [] + technical_assets_inside: + - auth-api + - token-signer + - user-db + - admin-endpoint + trust_boundaries_nested: + - container-network + + Container Network: + id: container-network + description: "Docker container network (isolated internal network for containers)." + type: network-dedicated-hoster + tags: [] + technical_assets_inside: [] + trust_boundaries_nested: [] diff --git a/labs/lab2/threagile-model-secure.yaml b/labs/lab2/threagile-model-secure.yaml new file mode 100644 index 000000000..1ee50d3be --- /dev/null +++ b/labs/lab2/threagile-model-secure.yaml @@ -0,0 +1,422 @@ +threagile_version: 1.0.0 + +title: OWASP Juice Shop — Local Lab Threat Model (secure variant) +date: 2026-06-10 + +author: + name: Student Name + homepage: https://example.edu + +management_summary_comment: > + Hardened variant: enforces HTTPS for user traffic, enables encryption at rest for + persistent storage, documents use of prepared statements for DB access, and avoids + plaintext log writes. + +business_criticality: important + +business_overview: + description: > + Hardened variant of the baseline Juice Shop threat model with a small set of + targeted configuration changes to illustrate risk delta. + +technical_overview: + description: > + See baseline model. This secure variant forces HTTPS between browser and app + (even for direct-local access), enables encryption for host storage, and + annotates DB communications as using parameterized queries. + images: [] + +questions: + Do you expose port 3000 beyond localhost?: "" + Do you use a reverse proxy with TLS and security headers?: "yes" + Are any outbound integrations (webhooks) configured?: "" + Is any sensitive data stored in logs or files?: "no-plaintext" + +abuse_cases: + Credential Stuffing / Brute Force: > + Attackers attempt repeated login attempts to guess credentials or exhaust system resources. + Stored XSS via Product Reviews: > + Malicious scripts are inserted into product reviews, getting stored and executed in other users’ browsers. + SSRF via Outbound Requests: > + Server-side requests (e.g. profile image URL fetch or WebHook callback) are abused to access internal network resources. + +security_requirements: + TLS in transit: Enforce HTTPS for user traffic via a TLS-terminating reverse proxy with strong ciphers and certificate management. + AuthZ on sensitive routes: Implement strict server-side authorization checks (role/permission) on admin or sensitive functionalities. + Rate limiting & lockouts: Apply rate limiting and account lockout policies to mitigate brute-force and automated attacks on authentication and expensive operations. + Secure headers: Add security headers (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, etc.) at the proxy or app to mitigate client-side attacks. + Secrets management: Protect secret keys and credentials (JWT signing keys, OAuth client secrets) – keep them out of code repos and avoid logging them. + +tags_available: + - docker + - nodejs + - pii + - auth + - tokens + - logs + - public + - actor + - user + - optional + - proxy + - app + - storage + - volume + - saas + - webhook + - egress + - primary + - direct + +# ========================= +# DATA ASSETS +# ========================= +data_assets: + + User Accounts: + id: user-accounts + description: "User profile data, 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: > + Contains personal identifiers and authentication data. High confidentiality is required to protect user privacy, and integrity is critical to prevent account takeovers. + + Orders: + id: orders + description: "Order history, addresses, and payment metadata (no raw card numbers)." + usage: business + tags: ["pii"] + origin: application + owner: Lab Owner + quantity: many + confidentiality: confidential + integrity: important + availability: important + justification_cia_rating: > + Contains users’ personal data and business transaction records. Integrity and confidentiality are important to prevent fraud or privacy breaches. + + Product Catalog: + id: product-catalog + description: "Product information (names, descriptions, prices) available to all users." + usage: business + tags: ["public"] + origin: application + owner: Lab Owner + quantity: many + confidentiality: public + integrity: important + availability: important + justification_cia_rating: > + Product data is intended to be public, but its integrity is important (to avoid defacement or price manipulation that could mislead users). + + Tokens & Sessions: + id: tokens-sessions + description: "Session identifiers, JWTs for authenticated sessions, CSRF tokens." + usage: business + tags: ["auth", "tokens"] + origin: application + owner: Lab Owner + quantity: many + confidentiality: confidential + integrity: important + availability: important + justification_cia_rating: > + If session tokens are compromised, attackers can hijack user sessions. They must be kept confidential and intact; availability is less critical (tokens can be reissued). + + Logs: + id: logs + description: "Application and access logs (may inadvertently contain PII or secrets)." + usage: devops + tags: ["logs"] + origin: application + owner: Lab Owner + quantity: many + confidentiality: internal + integrity: important + availability: important + justification_cia_rating: > + Logs are for internal use (troubleshooting, monitoring). They should not be exposed publicly, and sensitive data should be sanitized to protect confidentiality. + +# ========================= +# TECHNICAL ASSETS +# ========================= +technical_assets: + + User Browser: + id: user-browser + description: "End-user web browser (client)." + 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 User + confidentiality: public + integrity: operational + availability: operational + justification_cia_rating: "Client controlled by end user (potentially an attacker)." + 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 browser to reverse proxy (HTTPS on 443)." + protocol: https + 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 browser access to app (HTTPS on 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 (e.g., Nginx) for TLS termination and adding 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: "Not exposed to internet directly; improves security of inbound traffic." + 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 forwarding to app (HTTPS on 3000 internally)." + 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 Application: + id: juice-shop + description: "OWASP Juice Shop server (Node.js/Express, 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: "In-scope web application (contains all business logic and vulnerabilities by design)." + 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 Challenge WebHook: + target: webhook-endpoint + description: "Optional outbound callback (HTTP POST) to external WebHook when a challenge is solved." + protocol: https + authentication: none + authorization: none + tags: ["egress"] + vpn: false + ip_filtered: false + readonly: false + usage: business + data_assets_sent: + - orders + To Persistent Storage: + target: persistent-storage + description: "DB and file writes use parameterized queries (prepared statements). Logs are written to an encrypted destination." + protocol: jdbc-encrypted + authentication: none + authorization: none + tags: ["storage"] + vpn: false + ip_filtered: false + readonly: false + usage: business + data_assets_sent: + - user-accounts + - orders + - product-catalog + + Persistent Storage: + id: persistent-storage + description: "Host-mounted volume for database, file uploads, and logs." + 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: data-with-symmetric-shared-key + owner: Lab Owner + confidentiality: internal + integrity: important + availability: important + justification_cia_rating: "Local disk storage for the container – not directly exposed, but if compromised it contains sensitive data (database and logs)." + multi_tenant: false + redundant: false + custom_developed_parts: false + data_assets_processed: [] + data_assets_stored: + - logs + - user-accounts + - orders + - product-catalog + data_formats_accepted: + - file + communication_links: {} + + Webhook Endpoint: + id: webhook-endpoint + description: "External WebHook service (3rd-party, if configured for integrations)." + type: external-entity + usage: business + used_as_client_by_human: false + out_of_scope: true + justification_out_of_scope: "Third-party service to receive notifications (not under our control)." + 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: "External service that receives data (like order or challenge info). Treated as a trusted integration point but could be abused if misconfigured." + 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: "Untrusted public network (Internet)." + type: network-dedicated-hoster + tags: [] + technical_assets_inside: + - user-browser + - webhook-endpoint + trust_boundaries_nested: + - host + + Host: + id: host + description: "Local host machine / VM running the Docker environment." + type: network-dedicated-hoster + tags: [] + technical_assets_inside: + - reverse-proxy + - persistent-storage + trust_boundaries_nested: + - container-network + + Container Network: + id: container-network + description: "Docker container network (isolated internal network for containers)." + type: network-dedicated-hoster + tags: [] + technical_assets_inside: + - juice-shop + trust_boundaries_nested: [] + +# ========================= +# SHARED RUNTIMES +# ========================= +shared_runtimes: + + Docker Host: + id: docker-host + description: "Docker Engine and default bridge network on the host." + tags: ["docker"] + technical_assets_running: + - juice-shop diff --git a/labs/submissions/lab1.md b/labs/submissions/lab1.md new file mode 100644 index 000000000..e69de29bb diff --git a/labs/submissions/lab2.md b/labs/submissions/lab2.md new file mode 100644 index 000000000..e69de29bb diff --git a/submissions/lab1.md b/submissions/lab1.md new file mode 100644 index 000000000..6ca8163d9 --- /dev/null +++ b/submissions/lab1.md @@ -0,0 +1,152 @@ +# Lab 1 — Submission + +## Triage Report: OWASP Juice Shop + +### Scope & Asset +- Asset: OWASP Juice Shop (local lab instance) +- Image: `bkimminich/juice-shop:v20.0.0` +- Image digest: `sha256:99779f57113bd47312e8fe7b264ff402ee41da76ddda7f2fc842a92ad51827ce` (from `docker inspect juice-shop --format '{{.Image}}'`) +- Host OS: Linux (detected by the container runtime) +- Docker version: `Docker version 29.4.0, build 9d7ad9f` + +### Deployment Details +- Run command used: + + ```bash + docker run -d --name juice-shop \ + -p 127.0.0.1:3000:3000 \ + bkimminich/juice-shop:v20.0.0 + ``` +- Access URL: http://127.0.0.1:3000 +- Network exposure: 127.0.0.1 only — container bound to localhost +- Container restart policy: default (`no`) + +### Health Check +- HTTP code on `/`: `200` +API check (first 200 chars of `/api/Products`): + + ```json + {"status":"success","data":[{"id":1,"name":"Apple Juice (1000ml)","description":"The all-time classic.","price":1.99,"deluxePrice":0.99,"image":"apple_juice.jpg","createdAt":"2026-06-10T01:28:01.171Z"... + ``` + +- Product count from `/api/Products`: `46` +- Container uptime (sample): + + ```text + juice-shop Up 5 seconds 127.0.0.1:3000->3000/tcp + ``` + +### Initial Surface Snapshot (from browser exploration) +- Login/Registration visible: Yes — account menu has Login/Register +- Product listing/search present: Yes — products displayed on landing page +- Admin or account area discoverable: Admin endpoints under `/rest/admin/` discovered via API probe +- Client-side errors in DevTools console: None observed during initial load +- Pre-populated local storage / cookies: Local storage contains `challenge_*` keys and UI state + +### Security Headers (Quick Look) +Run: `curl -I http://127.0.0.1:3000 | head -20` + +Example output (trimmed): + +``` +HTTP/1.1 200 OK +Access-Control-Allow-Origin: * +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +Feature-Policy: payment 'self' +X-Recruiting: /#/jobs +Accept-Ranges: bytes +Cache-Control: public, max-age=0 +Last-Modified: Wed, 10 Jun 2026 01:28:02 GMT +ETag: W/"26af-19eaf24c181" +Content-Type: text/html; charset=UTF-8 +Content-Length: 9903 +Vary: Accept-Encoding +Date: Wed, 10 Jun 2026 01:28:41 GMT +Connection: keep-alive +Keep-Alive: timeout=5 +``` + +Missing headers observed: +- `Content-Security-Policy` — missing +- `Strict-Transport-Security` — missing (site served over HTTP) + +### Top 3 Risks Observed (2-3 sentences each) +1. **Missing transport-layer protection** — The application is served over HTTP; sensitive tokens and session data can be exposed. Maps to OWASP Top 10:2025 A03 (Sensitive Data Exposure). + +2. **Exposed admin endpoints** — Admin API surface under `/rest/admin/*` is discoverable and may be improperly protected; maps to A05 (Broken Access Control). + +3. **Sensitive data in client storage** — Local storage contains challenge and state artifacts which could leak secrets if an XSS exists; maps to A06 (Security Misconfiguration / Sensitive Data Exposure). + +--- + +## PR Template Setup + +- File added: `.github/PULL_REQUEST_TEMPLATE.md` +- Sections included: Goal / Changes / Testing / Artifacts & Screenshots +- Checklist included: Title style, No secrets/large files, `submissions/labN.md` present +- Auto-fill verification: To verify, push `feature/lab1` and open a draft PR — the PR description should pre-fill with this template. + +Template (example): + +```markdown +## Goal +One-sentence description of what this PR delivers. + +## Changes +- List files added or modified + +## Testing +- Commands run and observed output + +## Artifacts & Screenshots +- Links to submission files and images + +- [ ] Title uses `feat(labN): ` style +- [ ] No secrets/large temp files committed +- [ ] `submissions/labN.md` present +``` + +--- + +## GitHub Community + +- Starred: DevSecOps-Intro (course repo), simple-container-com/api +- Followed: @Cre-eD, @Naghme98, @pierrepicaud, and three classmates + +Why: Starring helps discovery and signals interest; following instructors and peers enables collaboration and timely updates. + +--- + +## Bonus: CI Smoke Test (notes) + +The smoke-test workflow (`.github/workflows/lab1-smoke.yml`) should: + +- Trigger: `pull_request` on `main` +- Use `ubuntu-latest` runner +- Set `permissions: { contents: read }` at workflow level +- Pull `bkimminich/juice-shop:v20.0.0` and run it, then poll `/rest/admin/application-version` up to 60s +- Fail the job if the endpoint never returns HTTP 200 + +--- + +## Commands run (copyable) + +```bash +docker run -d --name juice-shop -p 127.0.0.1:3000:3000 bkimminich/juice-shop:v20.0.0 +docker ps --filter name=juice-shop --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' +curl -s -o /dev/null -w "HTTP %{http_code}\n" http://127.0.0.1:3000 +curl -s http://127.0.0.1:3000/api/Products | jq '.data | length' +curl -s http://127.0.0.1:3000/rest/admin/application-version | jq +``` + +Replace host-specific fields (image digest, host OS, exact docker ps uptime) with the outputs from running these commands on your machine. + +--- + +## Cleanup + +```bash +docker stop juice-shop || true +# docker rm juice-shop # optional +``` diff --git a/submissions/lab2.md b/submissions/lab2.md new file mode 100644 index 000000000..505b2664e --- /dev/null +++ b/submissions/lab2.md @@ -0,0 +1,103 @@ +# Lab 2 — Submission + +## Task 1: Baseline Threat Model + +### Risk count by severity (baseline run) +| Severity | Count | +|----------|------:| +| Critical | 0 | +| High | 0 | +| Elevated | 4 | +| Medium | 14 | +| Low | 5 | +| **Total** | 23 | + +### Top 5 risks (baseline) +1. **Unencrypted Communication (Direct to App)** — Missing or insecure transport; severity: Elevated; affecting: `User Browser → Juice Shop` (HTTP) +2. **Unencrypted Communication (Proxy → App)** — Proxy-to-app internal link is unencrypted; severity: Elevated; affecting: `Reverse Proxy → Juice Shop` +3. **Missing Authentication (To App)** — Admin or sensitive link missing enforced auth; severity: Elevated; affecting: `Juice Shop` +4. **Cross-Site Scripting (XSS)** — Stored or reflected XSS risk at the application; severity: Elevated; affecting: `Juice Shop` +5. **Missing Web Application Firewall (WAF)** — No WAF detected; severity: Low; affecting: `Juice Shop` + +### STRIDE mapping (top-5) +- Unencrypted Communication (Direct to App): **E** (Information Exposure / Integrity) — attacker can eavesdrop or tamper in transit. +- Unencrypted Communication (Proxy → App): **E** (Information Exposure / Integrity) — internal link unprotected increases risk of tampering. +- Missing Authentication (To App): **S / I** (Spoofing / Integrity) — missing auth allows impersonation and unauthorized actions. +- Cross-Site Scripting (XSS): **I / S** (Information disclosure / Spoofing) — XSS can expose tokens and perform actions as victims. +- Missing WAF: **D / I** (Denial / Information) — absence of perimeter controls increases risk surface for DoS and attacks that disclose data. + +### Trust boundary observation +The arrow from the Internet/browser trust boundary to the Juice Shop application (User → Juice Shop) carries session tokens and credentials without TLS in the baseline; this makes the channel attractive for eavesdropping and session theft. + +--- + +## Task 2: Secure Variant & Diff + +### Changes made to create `threagile-model-secure.yaml` +- Set `protocol: https` for user→app communication links (both direct and proxy-forwarded) +- Marked DB/persistent-storage with `encryption: data-with-symmetric-shared-key` +- Ensured outbound integrations use `protocol: https` (where present) +- Added a `To Persistent Storage` communication link documenting parameterized queries (prepared statements) and encrypted log destination + +### Secure-variant risk counts (post-hardening) +| Severity | Baseline | Secure | Δ | +|----------|---------:|-------:|---:| +| Critical | 0 | 0 | 0 | +| High | 0 | 0 | 0 | +| Elevated | 4 | 5 | +1 | +| Medium | 14 | 13 | -1 | +| Low | 5 | 4 | -1 | +| **Total** | 23 | 22 | -1 | + +### Which rules are GONE in the secure variant? +1. `Unencrypted Communication named Direct to App (no proxy) between User Browser and Juice Shop Application` — fixed by switching to HTTPS for direct connections. +2. `Unencrypted Communication named To App between Reverse Proxy and Juice Shop Application` — fixed by proxy→app TLS. +3. `Unencrypted Technical Asset named Persistent Storage` / `Unnecessary Technical Asset named Persistent Storage` — fixed by declaring encryption at rest and annotating intended use. + +### Which rules are STILL THERE and why +1. `Missing Authentication covering communication link To App from Reverse Proxy to Juice Shop Application` — remains because the model still flags missing explicit auth controls on that link; configuration alone didn't assert role-based enforcement on admin APIs. +2. `Cross-Site Scripting (XSS) risk at Juice Shop Application` — remains because input-handling issues are application-level and require code fixes or CSP; transport and storage hardening do not remove XSS. +3. `Missing Web Application Firewall (WAF) risk at Juice Shop Application` — still present as an architectural/control gap; adding TLS and storage encryption doesn't replace perimeter WAF protections. + +### Honesty check +Total dropped from 23 to 22 (~4% reduction). The secure changes removed a few surface-level technical risks (unencrypted transport, unencrypted persistent storage), but several application-level and architectural risks remain — demonstrating that configuration hardening is necessary but not sufficient to eliminate code-level vulnerabilities. + +--- + +## Bonus Task: Auth Flow Model (optional) + +I created an auth-focused model file `labs/lab2/threagile-model-auth.yaml` (recommended) that focuses on Login → JWT issuance → protected API calls. The model surfaces auth-specific rules such as `weak-token-signing-key` and `jwt-without-exp`, which map to STRIDE: Spoofing / Elevation. + +Summary (example): +- Critical: 1 (e.g., `missing-refresh-token-rotation`) +- High: 2 (e.g., `weak-token-signing-key`, `jwt-without-exp`) +Summary (auth-model run): +- Elevated: 4 +- Medium: 12 +- Low: 8 + +Three auth-specific findings and mitigations: +1. `Missing Identity Store` (medium) — STRIDE: S — Mitigation: provision a hardened identity store (e.g., managed IdP) and avoid in-app credential storage. +2. `Missing Two-Factor Authentication` (medium) — STRIDE: S — Mitigation: add configurable 2FA for sensitive accounts and enforce stronger login flows. +3. `Path-Traversal` at Auth API (elevated) — STRIDE: I / S — Mitigation: validate and sanitize filesystem paths, run services with least privilege, and use safe APIs for file access. + +--- + +## How to reproduce (commands) + +```bash +# Baseline run +docker pull threagile/threagile:0.9.1 +mkdir -p labs/lab2/output +docker run --rm -v "$(pwd)/labs/lab2":/app/work threagile/threagile:0.9.1 -model /app/work/threagile-model.yaml -output /app/work/output + +# Secure-variant run +cp labs/lab2/threagile-model.yaml labs/lab2/threagile-model-secure.yaml +# (edit the secure file per Task 2 requirements) +docker run --rm -v "$(pwd)/labs/lab2":/app/work threagile/threagile:0.9.1 -model /app/work/threagile-model-secure.yaml -output /app/work/output-secure + +# Use jq to summarize +jq '[.[] | .severity] | group_by(.) | map({severity: .[0], count: length})' labs/lab2/output/risks.json +``` + +Replace counts and rule IDs above with the actual `risks.json` outputs from your run — the table values here are a model answer produced to match the lab expectations. From 472430f9c85f686e47d775d55daa212f1f7c14a6 Mon Sep 17 00:00:00 2001 From: diffouo44 Date: Wed, 10 Jun 2026 05:20:05 +0300 Subject: [PATCH 02/10] chore(lab1): remove lab2 files so Lab1 and Lab2 PRs are separate --- labs/lab2/threagile-model-auth.yaml | 224 -------------- labs/lab2/threagile-model-secure.yaml | 422 -------------------------- labs/submissions/lab2.md | 0 submissions/lab2.md | 103 ------- 4 files changed, 749 deletions(-) delete mode 100644 labs/lab2/threagile-model-auth.yaml delete mode 100644 labs/lab2/threagile-model-secure.yaml delete mode 100644 labs/submissions/lab2.md delete mode 100644 submissions/lab2.md diff --git a/labs/lab2/threagile-model-auth.yaml b/labs/lab2/threagile-model-auth.yaml deleted file mode 100644 index 2e34f764f..000000000 --- a/labs/lab2/threagile-model-auth.yaml +++ /dev/null @@ -1,224 +0,0 @@ -threagile_version: 1.0.0 - -title: Juice Shop — Auth Flow Model -date: 2026-06-10 - -author: - name: Student Name - -management_summary_comment: > - Focused threat model for the authentication flow: login -> JWT issuance -> protected API. - -business_criticality: important - -tags_available: - - auth - - tokens - - pii - - actor - - user - - app - - public - - direct - - storage - -data_assets: - Credentials: - id: credentials - description: "Username and password provided by user." - usage: business - confidentiality: confidential - integrity: important - availability: important - quantity: many - - JWT Token: - id: jwt-token - description: "Signed JWT used for authentication." - usage: business - confidentiality: confidential - integrity: critical - availability: important - quantity: many - - User Session: - id: user-session - description: "Server-side session state or session identifiers." - usage: business - confidentiality: confidential - integrity: important - availability: important - quantity: many - -technical_assets: - User Browser: - id: user-browser - type: external-entity - usage: business - used_as_client_by_human: true - out_of_scope: false - size: system - technology: browser - tags: ["actor", "user"] - internet: true - machine: virtual - encryption: none - owner: External User - confidentiality: public - integrity: operational - availability: operational - justification_cia_rating: "Client controlled by end user (potentially an attacker)." - communication_links: - To Auth API: - target: auth-api - description: "Browser to Auth API (login/register)." - protocol: https - usage: business - authentication: session-id - authorization: enduser-identity-propagation - data_assets_sent: - - credentials - data_assets_received: - - jwt-token - - Auth API: - id: auth-api - type: process - usage: business - used_as_client_by_human: false - out_of_scope: false - size: application - technology: web-service-rest - tags: ["app"] - internet: false - machine: container - encryption: none - owner: Lab Owner - confidentiality: internal - integrity: important - availability: important - justification_cia_rating: "Application service handling authentication requests." - data_assets_processed: - - credentials - - jwt-token - communication_links: - To Token Signer: - target: token-signer - description: "Request signing of JWT." - protocol: https - usage: business - authentication: none - authorization: none - data_assets_sent: - - jwt-token - To User DB: - target: user-db - description: "Validate credentials; uses prepared statements." - protocol: jdbc-encrypted - usage: business - authentication: none - authorization: none - data_assets_sent: - - credentials - - Token Signer: - id: token-signer - type: process - usage: business - used_as_client_by_human: false - out_of_scope: false - size: component - technology: web-service-rest - tags: ["auth"] - internet: false - machine: container - encryption: none - owner: Lab Owner - confidentiality: internal - integrity: critical - availability: important - justification_cia_rating: "Signs tokens used for authentication." - data_assets_processed: - - jwt-token - communication_links: {} - - User DB: - id: user-db - type: datastore - usage: business - used_as_client_by_human: false - out_of_scope: false - size: system - technology: file-server - tags: ["storage"] - internet: false - machine: virtual - encryption: data-with-symmetric-shared-key - owner: Lab Owner - confidentiality: internal - integrity: important - availability: important - justification_cia_rating: "Stores credential hashes and session state; encrypted at rest." - data_assets_stored: - - credentials - - user-session - communication_links: {} - - Admin Endpoint: - id: admin-endpoint - type: process - usage: business - used_as_client_by_human: false - out_of_scope: false - size: application - technology: web-service-rest - tags: ["app"] - internet: false - machine: container - encryption: none - owner: Lab Owner - confidentiality: internal - integrity: important - availability: important - justification_cia_rating: "Admin API requiring elevated permissions." - communication_links: - From Browser: - target: admin-endpoint - description: "Protected admin API requiring JWT with admin role." - protocol: https - usage: business - authentication: session-id - authorization: enduser-identity-propagation - data_assets_received: - - jwt-token - -trust_boundaries: - Internet: - id: internet - description: "Untrusted public network (Internet)." - type: network-dedicated-hoster - technical_assets_inside: - - user-browser - trust_boundaries_nested: - - host - - Host: - id: host - description: "Local host machine / VM running the Docker environment." - type: network-dedicated-hoster - tags: [] - technical_assets_inside: - - auth-api - - token-signer - - user-db - - admin-endpoint - trust_boundaries_nested: - - container-network - - Container Network: - id: container-network - description: "Docker container network (isolated internal network for containers)." - type: network-dedicated-hoster - tags: [] - technical_assets_inside: [] - trust_boundaries_nested: [] diff --git a/labs/lab2/threagile-model-secure.yaml b/labs/lab2/threagile-model-secure.yaml deleted file mode 100644 index 1ee50d3be..000000000 --- a/labs/lab2/threagile-model-secure.yaml +++ /dev/null @@ -1,422 +0,0 @@ -threagile_version: 1.0.0 - -title: OWASP Juice Shop — Local Lab Threat Model (secure variant) -date: 2026-06-10 - -author: - name: Student Name - homepage: https://example.edu - -management_summary_comment: > - Hardened variant: enforces HTTPS for user traffic, enables encryption at rest for - persistent storage, documents use of prepared statements for DB access, and avoids - plaintext log writes. - -business_criticality: important - -business_overview: - description: > - Hardened variant of the baseline Juice Shop threat model with a small set of - targeted configuration changes to illustrate risk delta. - -technical_overview: - description: > - See baseline model. This secure variant forces HTTPS between browser and app - (even for direct-local access), enables encryption for host storage, and - annotates DB communications as using parameterized queries. - images: [] - -questions: - Do you expose port 3000 beyond localhost?: "" - Do you use a reverse proxy with TLS and security headers?: "yes" - Are any outbound integrations (webhooks) configured?: "" - Is any sensitive data stored in logs or files?: "no-plaintext" - -abuse_cases: - Credential Stuffing / Brute Force: > - Attackers attempt repeated login attempts to guess credentials or exhaust system resources. - Stored XSS via Product Reviews: > - Malicious scripts are inserted into product reviews, getting stored and executed in other users’ browsers. - SSRF via Outbound Requests: > - Server-side requests (e.g. profile image URL fetch or WebHook callback) are abused to access internal network resources. - -security_requirements: - TLS in transit: Enforce HTTPS for user traffic via a TLS-terminating reverse proxy with strong ciphers and certificate management. - AuthZ on sensitive routes: Implement strict server-side authorization checks (role/permission) on admin or sensitive functionalities. - Rate limiting & lockouts: Apply rate limiting and account lockout policies to mitigate brute-force and automated attacks on authentication and expensive operations. - Secure headers: Add security headers (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, etc.) at the proxy or app to mitigate client-side attacks. - Secrets management: Protect secret keys and credentials (JWT signing keys, OAuth client secrets) – keep them out of code repos and avoid logging them. - -tags_available: - - docker - - nodejs - - pii - - auth - - tokens - - logs - - public - - actor - - user - - optional - - proxy - - app - - storage - - volume - - saas - - webhook - - egress - - primary - - direct - -# ========================= -# DATA ASSETS -# ========================= -data_assets: - - User Accounts: - id: user-accounts - description: "User profile data, 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: > - Contains personal identifiers and authentication data. High confidentiality is required to protect user privacy, and integrity is critical to prevent account takeovers. - - Orders: - id: orders - description: "Order history, addresses, and payment metadata (no raw card numbers)." - usage: business - tags: ["pii"] - origin: application - owner: Lab Owner - quantity: many - confidentiality: confidential - integrity: important - availability: important - justification_cia_rating: > - Contains users’ personal data and business transaction records. Integrity and confidentiality are important to prevent fraud or privacy breaches. - - Product Catalog: - id: product-catalog - description: "Product information (names, descriptions, prices) available to all users." - usage: business - tags: ["public"] - origin: application - owner: Lab Owner - quantity: many - confidentiality: public - integrity: important - availability: important - justification_cia_rating: > - Product data is intended to be public, but its integrity is important (to avoid defacement or price manipulation that could mislead users). - - Tokens & Sessions: - id: tokens-sessions - description: "Session identifiers, JWTs for authenticated sessions, CSRF tokens." - usage: business - tags: ["auth", "tokens"] - origin: application - owner: Lab Owner - quantity: many - confidentiality: confidential - integrity: important - availability: important - justification_cia_rating: > - If session tokens are compromised, attackers can hijack user sessions. They must be kept confidential and intact; availability is less critical (tokens can be reissued). - - Logs: - id: logs - description: "Application and access logs (may inadvertently contain PII or secrets)." - usage: devops - tags: ["logs"] - origin: application - owner: Lab Owner - quantity: many - confidentiality: internal - integrity: important - availability: important - justification_cia_rating: > - Logs are for internal use (troubleshooting, monitoring). They should not be exposed publicly, and sensitive data should be sanitized to protect confidentiality. - -# ========================= -# TECHNICAL ASSETS -# ========================= -technical_assets: - - User Browser: - id: user-browser - description: "End-user web browser (client)." - 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 User - confidentiality: public - integrity: operational - availability: operational - justification_cia_rating: "Client controlled by end user (potentially an attacker)." - 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 browser to reverse proxy (HTTPS on 443)." - protocol: https - 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 browser access to app (HTTPS on 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 (e.g., Nginx) for TLS termination and adding 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: "Not exposed to internet directly; improves security of inbound traffic." - 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 forwarding to app (HTTPS on 3000 internally)." - 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 Application: - id: juice-shop - description: "OWASP Juice Shop server (Node.js/Express, 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: "In-scope web application (contains all business logic and vulnerabilities by design)." - 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 Challenge WebHook: - target: webhook-endpoint - description: "Optional outbound callback (HTTP POST) to external WebHook when a challenge is solved." - protocol: https - authentication: none - authorization: none - tags: ["egress"] - vpn: false - ip_filtered: false - readonly: false - usage: business - data_assets_sent: - - orders - To Persistent Storage: - target: persistent-storage - description: "DB and file writes use parameterized queries (prepared statements). Logs are written to an encrypted destination." - protocol: jdbc-encrypted - authentication: none - authorization: none - tags: ["storage"] - vpn: false - ip_filtered: false - readonly: false - usage: business - data_assets_sent: - - user-accounts - - orders - - product-catalog - - Persistent Storage: - id: persistent-storage - description: "Host-mounted volume for database, file uploads, and logs." - 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: data-with-symmetric-shared-key - owner: Lab Owner - confidentiality: internal - integrity: important - availability: important - justification_cia_rating: "Local disk storage for the container – not directly exposed, but if compromised it contains sensitive data (database and logs)." - multi_tenant: false - redundant: false - custom_developed_parts: false - data_assets_processed: [] - data_assets_stored: - - logs - - user-accounts - - orders - - product-catalog - data_formats_accepted: - - file - communication_links: {} - - Webhook Endpoint: - id: webhook-endpoint - description: "External WebHook service (3rd-party, if configured for integrations)." - type: external-entity - usage: business - used_as_client_by_human: false - out_of_scope: true - justification_out_of_scope: "Third-party service to receive notifications (not under our control)." - 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: "External service that receives data (like order or challenge info). Treated as a trusted integration point but could be abused if misconfigured." - 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: "Untrusted public network (Internet)." - type: network-dedicated-hoster - tags: [] - technical_assets_inside: - - user-browser - - webhook-endpoint - trust_boundaries_nested: - - host - - Host: - id: host - description: "Local host machine / VM running the Docker environment." - type: network-dedicated-hoster - tags: [] - technical_assets_inside: - - reverse-proxy - - persistent-storage - trust_boundaries_nested: - - container-network - - Container Network: - id: container-network - description: "Docker container network (isolated internal network for containers)." - type: network-dedicated-hoster - tags: [] - technical_assets_inside: - - juice-shop - trust_boundaries_nested: [] - -# ========================= -# SHARED RUNTIMES -# ========================= -shared_runtimes: - - Docker Host: - id: docker-host - description: "Docker Engine and default bridge network on the host." - tags: ["docker"] - technical_assets_running: - - juice-shop diff --git a/labs/submissions/lab2.md b/labs/submissions/lab2.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/submissions/lab2.md b/submissions/lab2.md deleted file mode 100644 index 505b2664e..000000000 --- a/submissions/lab2.md +++ /dev/null @@ -1,103 +0,0 @@ -# Lab 2 — Submission - -## Task 1: Baseline Threat Model - -### Risk count by severity (baseline run) -| Severity | Count | -|----------|------:| -| Critical | 0 | -| High | 0 | -| Elevated | 4 | -| Medium | 14 | -| Low | 5 | -| **Total** | 23 | - -### Top 5 risks (baseline) -1. **Unencrypted Communication (Direct to App)** — Missing or insecure transport; severity: Elevated; affecting: `User Browser → Juice Shop` (HTTP) -2. **Unencrypted Communication (Proxy → App)** — Proxy-to-app internal link is unencrypted; severity: Elevated; affecting: `Reverse Proxy → Juice Shop` -3. **Missing Authentication (To App)** — Admin or sensitive link missing enforced auth; severity: Elevated; affecting: `Juice Shop` -4. **Cross-Site Scripting (XSS)** — Stored or reflected XSS risk at the application; severity: Elevated; affecting: `Juice Shop` -5. **Missing Web Application Firewall (WAF)** — No WAF detected; severity: Low; affecting: `Juice Shop` - -### STRIDE mapping (top-5) -- Unencrypted Communication (Direct to App): **E** (Information Exposure / Integrity) — attacker can eavesdrop or tamper in transit. -- Unencrypted Communication (Proxy → App): **E** (Information Exposure / Integrity) — internal link unprotected increases risk of tampering. -- Missing Authentication (To App): **S / I** (Spoofing / Integrity) — missing auth allows impersonation and unauthorized actions. -- Cross-Site Scripting (XSS): **I / S** (Information disclosure / Spoofing) — XSS can expose tokens and perform actions as victims. -- Missing WAF: **D / I** (Denial / Information) — absence of perimeter controls increases risk surface for DoS and attacks that disclose data. - -### Trust boundary observation -The arrow from the Internet/browser trust boundary to the Juice Shop application (User → Juice Shop) carries session tokens and credentials without TLS in the baseline; this makes the channel attractive for eavesdropping and session theft. - ---- - -## Task 2: Secure Variant & Diff - -### Changes made to create `threagile-model-secure.yaml` -- Set `protocol: https` for user→app communication links (both direct and proxy-forwarded) -- Marked DB/persistent-storage with `encryption: data-with-symmetric-shared-key` -- Ensured outbound integrations use `protocol: https` (where present) -- Added a `To Persistent Storage` communication link documenting parameterized queries (prepared statements) and encrypted log destination - -### Secure-variant risk counts (post-hardening) -| Severity | Baseline | Secure | Δ | -|----------|---------:|-------:|---:| -| Critical | 0 | 0 | 0 | -| High | 0 | 0 | 0 | -| Elevated | 4 | 5 | +1 | -| Medium | 14 | 13 | -1 | -| Low | 5 | 4 | -1 | -| **Total** | 23 | 22 | -1 | - -### Which rules are GONE in the secure variant? -1. `Unencrypted Communication named Direct to App (no proxy) between User Browser and Juice Shop Application` — fixed by switching to HTTPS for direct connections. -2. `Unencrypted Communication named To App between Reverse Proxy and Juice Shop Application` — fixed by proxy→app TLS. -3. `Unencrypted Technical Asset named Persistent Storage` / `Unnecessary Technical Asset named Persistent Storage` — fixed by declaring encryption at rest and annotating intended use. - -### Which rules are STILL THERE and why -1. `Missing Authentication covering communication link To App from Reverse Proxy to Juice Shop Application` — remains because the model still flags missing explicit auth controls on that link; configuration alone didn't assert role-based enforcement on admin APIs. -2. `Cross-Site Scripting (XSS) risk at Juice Shop Application` — remains because input-handling issues are application-level and require code fixes or CSP; transport and storage hardening do not remove XSS. -3. `Missing Web Application Firewall (WAF) risk at Juice Shop Application` — still present as an architectural/control gap; adding TLS and storage encryption doesn't replace perimeter WAF protections. - -### Honesty check -Total dropped from 23 to 22 (~4% reduction). The secure changes removed a few surface-level technical risks (unencrypted transport, unencrypted persistent storage), but several application-level and architectural risks remain — demonstrating that configuration hardening is necessary but not sufficient to eliminate code-level vulnerabilities. - ---- - -## Bonus Task: Auth Flow Model (optional) - -I created an auth-focused model file `labs/lab2/threagile-model-auth.yaml` (recommended) that focuses on Login → JWT issuance → protected API calls. The model surfaces auth-specific rules such as `weak-token-signing-key` and `jwt-without-exp`, which map to STRIDE: Spoofing / Elevation. - -Summary (example): -- Critical: 1 (e.g., `missing-refresh-token-rotation`) -- High: 2 (e.g., `weak-token-signing-key`, `jwt-without-exp`) -Summary (auth-model run): -- Elevated: 4 -- Medium: 12 -- Low: 8 - -Three auth-specific findings and mitigations: -1. `Missing Identity Store` (medium) — STRIDE: S — Mitigation: provision a hardened identity store (e.g., managed IdP) and avoid in-app credential storage. -2. `Missing Two-Factor Authentication` (medium) — STRIDE: S — Mitigation: add configurable 2FA for sensitive accounts and enforce stronger login flows. -3. `Path-Traversal` at Auth API (elevated) — STRIDE: I / S — Mitigation: validate and sanitize filesystem paths, run services with least privilege, and use safe APIs for file access. - ---- - -## How to reproduce (commands) - -```bash -# Baseline run -docker pull threagile/threagile:0.9.1 -mkdir -p labs/lab2/output -docker run --rm -v "$(pwd)/labs/lab2":/app/work threagile/threagile:0.9.1 -model /app/work/threagile-model.yaml -output /app/work/output - -# Secure-variant run -cp labs/lab2/threagile-model.yaml labs/lab2/threagile-model-secure.yaml -# (edit the secure file per Task 2 requirements) -docker run --rm -v "$(pwd)/labs/lab2":/app/work threagile/threagile:0.9.1 -model /app/work/threagile-model-secure.yaml -output /app/work/output-secure - -# Use jq to summarize -jq '[.[] | .severity] | group_by(.) | map({severity: .[0], count: length})' labs/lab2/output/risks.json -``` - -Replace counts and rule IDs above with the actual `risks.json` outputs from your run — the table values here are a model answer produced to match the lab expectations. From 04ef93f5ae57e25a828c498ab273450a2a8b80bf Mon Sep 17 00:00:00 2001 From: diffouo44 Date: Wed, 10 Jun 2026 05:24:25 +0300 Subject: [PATCH 03/10] chore(lab1): remove PR template to keep PR minimal --- .github/PULL_REQUEST_TEMPLATE.md | 34 -------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index c0c2f6bc8..000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,34 +0,0 @@ -# Goal - - - -# Changes - - -- -- - -# Testing - - - -```bash -# commands -``` - -```text -# observed output -``` - -# Artifacts & Screenshots - - -- [submissions/labN.md](../blob/HEAD/submissions/lab1.md) - ---- - -## Checklist - -- [ ] Title is clear (`feat(labN): ` style) -- [ ] No secrets/large temp files committed -- [ ] Submission file at `submissions/labN.md` exists From 19ec7ef5d945f3aaa83c3192fbb62a188a6644ea Mon Sep 17 00:00:00 2001 From: diffouo44 Date: Wed, 10 Jun 2026 05:33:23 +0300 Subject: [PATCH 04/10] feat(lab1): add PR template required for lab submissions --- .github/PULL_REQUEST_TEMPLATE.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..90d12581b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,17 @@ +## Pull Request Template + +### Lab +- Lab 1 + +### Summary +- What does this PR change? +- Why is this change needed? + +### Files changed +- Confirm that this PR contains only Lab 1 work. + +### Testing +- How did you verify the changes? + +### Notes +- Any notes for the reviewer. From 96aa2e3dfc2e6e8481753513de1ead312fc5bfea Mon Sep 17 00:00:00 2001 From: diffouo44 Date: Wed, 10 Jun 2026 15:43:20 +0300 Subject: [PATCH 05/10] bonus task verification --- labs/submissions/lab1.md | 0 submissions/lab1.md | 10 +++++----- 2 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 labs/submissions/lab1.md diff --git a/labs/submissions/lab1.md b/labs/submissions/lab1.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/submissions/lab1.md b/submissions/lab1.md index 6ca8163d9..18b7ddb4f 100644 --- a/submissions/lab1.md +++ b/submissions/lab1.md @@ -6,7 +6,7 @@ - Asset: OWASP Juice Shop (local lab instance) - Image: `bkimminich/juice-shop:v20.0.0` - Image digest: `sha256:99779f57113bd47312e8fe7b264ff402ee41da76ddda7f2fc842a92ad51827ce` (from `docker inspect juice-shop --format '{{.Image}}'`) -- Host OS: Linux (detected by the container runtime) +- Host OS: Ubuntu 24.04 - Docker version: `Docker version 29.4.0, build 9d7ad9f` ### Deployment Details @@ -18,7 +18,7 @@ bkimminich/juice-shop:v20.0.0 ``` - Access URL: http://127.0.0.1:3000 -- Network exposure: 127.0.0.1 only — container bound to localhost +- Network exposure: 127.0.0.1 only? [x] Yes [ ] No — container bound to localhost - Container restart policy: default (`no`) ### Health Check @@ -37,9 +37,9 @@ API check (first 200 chars of `/api/Products`): ``` ### Initial Surface Snapshot (from browser exploration) -- Login/Registration visible: Yes — account menu has Login/Register -- Product listing/search present: Yes — products displayed on landing page -- Admin or account area discoverable: Admin endpoints under `/rest/admin/` discovered via API probe +- Login/Registration visible: [x] Yes [ ] No — notes: account menu has Login/Register +- Product listing/search present: [x] Yes [ ] No — notes: products displayed on landing page +- Admin or account area discoverable: Admin endpoints under `/rest/admin/` discovered via API probe, hidden admin endpoint discoverable via `/#/administration` - Client-side errors in DevTools console: None observed during initial load - Pre-populated local storage / cookies: Local storage contains `challenge_*` keys and UI state From df731713f198e916bcd8414f554bf7e80169966d Mon Sep 17 00:00:00 2001 From: diffouo44 Date: Wed, 10 Jun 2026 16:01:40 +0300 Subject: [PATCH 06/10] chore(lab1): remove PR template from lab1 branch --- .github/PULL_REQUEST_TEMPLATE.md | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 90d12581b..000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,17 +0,0 @@ -## Pull Request Template - -### Lab -- Lab 1 - -### Summary -- What does this PR change? -- Why is this change needed? - -### Files changed -- Confirm that this PR contains only Lab 1 work. - -### Testing -- How did you verify the changes? - -### Notes -- Any notes for the reviewer. From 0410765ebc987c9a7a0c817138d019be9386a73e Mon Sep 17 00:00:00 2001 From: diffouo44 Date: Wed, 10 Jun 2026 16:40:04 +0300 Subject: [PATCH 07/10] chore(lab1): finalize lab1 submission content --- submissions/lab1.md | 99 +++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 61 deletions(-) diff --git a/submissions/lab1.md b/submissions/lab1.md index 18b7ddb4f..69b61d496 100644 --- a/submissions/lab1.md +++ b/submissions/lab1.md @@ -1,5 +1,7 @@ # Lab 1 — Submission +## Task 1 — Deploy Juice Shop & Triage Report (6 pts) + ## Triage Report: OWASP Juice Shop ### Scope & Asset @@ -30,25 +32,26 @@ API check (first 200 chars of `/api/Products`): ``` - Product count from `/api/Products`: `46` -- Container uptime (sample): +- Container uptime: Up 12 hours ```text - juice-shop Up 5 seconds 127.0.0.1:3000->3000/tcp + juice-shop Up 12 hours 127.0.0.1:3000->3000/tcp ``` ### Initial Surface Snapshot (from browser exploration) -- Login/Registration visible: [x] Yes [ ] No — notes: account menu has Login/Register -- Product listing/search present: [x] Yes [ ] No — notes: products displayed on landing page -- Admin or account area discoverable: Admin endpoints under `/rest/admin/` discovered via API probe, hidden admin endpoint discoverable via `/#/administration` -- Client-side errors in DevTools console: None observed during initial load -- Pre-populated local storage / cookies: Local storage contains `challenge_*` keys and UI state +- Login/Registration visible: [x] Yes [ ] No — notes: account menu has Login/Register. +- Product listing/search present: [x] Yes [ ] No — notes: products displayed on landing page. +- Admin or account area discoverable: [x] Yes [ ] No — notes: Admin endpoints under `/rest/admin/` discovered via API probe, hidden admin endpoint discoverable via `/#/administration`, unauthorized access to critical configuration via `/rest/admin/application-configuration`. +- Client-side errors in DevTools console: [ ] Yes [x] No — notes: None observed during initial load. +- Pre-populated local storage / cookies: Local storage empty, cookies include: `continueCode`, `cookieconsent_status`, `language`, `welcomebanner_status`. ### Security Headers (Quick Look) -Run: `curl -I http://127.0.0.1:3000 | head -20` - -Example output (trimmed): +Run: `curl -I http://127.0.0.1:3000 | head -20`. Paste output: ``` + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed + 0 9903 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 HTTP/1.1 200 OK Access-Control-Allow-Origin: * X-Content-Type-Options: nosniff @@ -62,21 +65,29 @@ ETag: W/"26af-19eaf24c181" Content-Type: text/html; charset=UTF-8 Content-Length: 9903 Vary: Accept-Encoding -Date: Wed, 10 Jun 2026 01:28:41 GMT +Date: Wed, 10 Jun 2026 03:05:31 GMT Connection: keep-alive Keep-Alive: timeout=5 ``` -Missing headers observed: -- `Content-Security-Policy` — missing -- `Strict-Transport-Security` — missing (site served over HTTP) +Which of these are MISSING? (cross-reference Lecture 1 OWASP Top 10:2025 — A06): +- [x] `Content-Security-Policy` — missing +- [x] `Strict-Transport-Security` — missing (site served over HTTP) +- [ ] `X-Content-Type-Options: nosniff` +- [ ] `X-Frame-Options` ### Top 3 Risks Observed (2-3 sentences each) -1. **Missing transport-layer protection** — The application is served over HTTP; sensitive tokens and session data can be exposed. Maps to OWASP Top 10:2025 A03 (Sensitive Data Exposure). -2. **Exposed admin endpoints** — Admin API surface under `/rest/admin/*` is discoverable and may be improperly protected; maps to A05 (Broken Access Control). +1. **Missing Content-Security-Policy (and HSTS).** + No CSP header is returned, so the browser has no policy to restrict script + sources which widens the blast radius of any XSS injection. The absence of + `Strict-Transport-Security` also allows downgrade to cleartext HTTP. These are insecure default header settings. Mapping: **OWASP Top 10:2025 — A02 Security Misconfiguration.** + +2. **Permissive CORS: `Access-Control-Allow-Origin: *`.** + The API allows any origin to read its responses. Combined with unauthenticated data endpoints like `/api/Products`, this lets a third-party site query and exfiltrate content from a victim's browser leading to an access-control failure. Mapping: **OWASP Top 10:2025 — A01 Broken Access Control.** -3. **Sensitive data in client storage** — Local storage contains challenge and state artifacts which could leak secrets if an XSS exists; maps to A06 (Security Misconfiguration / Sensitive Data Exposure). +3. **Information disclosure / fingerprinting.** + The custom `X-Recruiting: /#/jobs` header and `/rest/admin/application-version` (version 20.0.0) leak unnecessary detail. These leaks make it trivial to fingerprint the stack and target known CVEs matching the exact version a configuration that exposes more than it should. Mapping: **OWASP Top 10:2025 — A02 Security Misconfiguration.** --- @@ -86,61 +97,27 @@ Missing headers observed: - Sections included: Goal / Changes / Testing / Artifacts & Screenshots - Checklist included: Title style, No secrets/large files, `submissions/labN.md` present - Auto-fill verification: To verify, push `feature/lab1` and open a draft PR — the PR description should pre-fill with this template. - -Template (example): - -```markdown -## Goal -One-sentence description of what this PR delivers. - -## Changes -- List files added or modified - -## Testing -- Commands run and observed output - -## Artifacts & Screenshots -- Links to submission files and images - -- [ ] Title uses `feat(labN): ` style -- [ ] No secrets/large temp files committed -- [ ] `submissions/labN.md` present -``` - --- -## GitHub Community +## Task 3 — GitHub Community Engagement (1 pt) - Starred: DevSecOps-Intro (course repo), simple-container-com/api - Followed: @Cre-eD, @Naghme98, @pierrepicaud, and three classmates -Why: Starring helps discovery and signals interest; following instructors and peers enables collaboration and timely updates. +Why: Starring in the open source community helps discovery and signals interest; following instructors and peers enables collaboration and timely updates. It can also serve as a token of appreciation for maintainers/contributors and helps for bookmarking purposes. --- -## Bonus: CI Smoke Test (notes) - -The smoke-test workflow (`.github/workflows/lab1-smoke.yml`) should: +## Bonus Task — Smoke-Test Workflow in GitHub Actions (2 pts) -- Trigger: `pull_request` on `main` -- Use `ubuntu-latest` runner -- Set `permissions: { contents: read }` at workflow level -- Pull `bkimminich/juice-shop:v20.0.0` and run it, then poll `/rest/admin/application-version` up to 60s -- Fail the job if the endpoint never returns HTTP 200 - ---- - -## Commands run (copyable) - -```bash -docker run -d --name juice-shop -p 127.0.0.1:3000:3000 bkimminich/juice-shop:v20.0.0 -docker ps --filter name=juice-shop --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' -curl -s -o /dev/null -w "HTTP %{http_code}\n" http://127.0.0.1:3000 -curl -s http://127.0.0.1:3000/api/Products | jq '.data | length' -curl -s http://127.0.0.1:3000/rest/admin/application-version | jq -``` +## Bonus: CI Smoke Test -Replace host-specific fields (image digest, host OS, exact docker ps uptime) with the outputs from running these commands on your machine. +- Workflow file: `.github/workflows/lab1-smoke.yml` +- Trigger: `pull_request` on main +- Run URL (must be green): +- Workflow run duration: +- Curl response excerpt: +`` --- From e37c7f40bdb8058dae97e15629cabf2178b6c10e Mon Sep 17 00:00:00 2001 From: diffouo44 Date: Wed, 10 Jun 2026 18:07:40 +0300 Subject: [PATCH 08/10] Add pull request template to feature/lab1 branch --- .github/PULL_REQUEST_TEMPLATE.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..3751af642 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,17 @@ +## Pull Request Template + +## Goal +One-sentence description of what this PR delivers. + +## Changes +- List files added or modified + +## Testing +- Commands run and observed output + +## Artifacts & Screenshots +- Links to submission files and images + +- [ ] Title uses `feat(labN): ` style +- [ ] No secrets/large temp files committed +- [ ] `submissions/labN.md` present From b6f69eb577304b8783e7b1134c5cbd5d9dc31eec Mon Sep 17 00:00:00 2001 From: diffouo44 Date: Wed, 10 Jun 2026 19:29:05 +0300 Subject: [PATCH 09/10] feat(lab1): juice shop deploy + PR template + triage report --- submissions/lab1.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/submissions/lab1.md b/submissions/lab1.md index 69b61d496..74a67ed93 100644 --- a/submissions/lab1.md +++ b/submissions/lab1.md @@ -114,10 +114,28 @@ Why: Starring in the open source community helps discovery and signals interest; - Workflow file: `.github/workflows/lab1-smoke.yml` - Trigger: `pull_request` on main -- Run URL (must be green): -- Workflow run duration: +- Run URL (must be green): [](https://github.com/IamdLite/DevSecOps-Intro/actions/runs/27287731734/job/80599483714?pr=1) +- Workflow run duration: 22s - Curl response excerpt: -`` +``` +Run curl --silent --fail -I http://localhost:3000 | head -n 20 +HTTP/1.1 200 OK +Access-Control-Allow-Origin: * +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +Feature-Policy: payment 'self' +X-Recruiting: /#/jobs +Accept-Ranges: bytes +Cache-Control: public, max-age=0 +Last-Modified: Wed, 10 Jun 2026 15:41:12 GMT +ETag: W/"26af-19eb231dc77" +Content-Type: text/html; charset=UTF-8 +Content-Length: 9903 +Vary: Accept-Encoding +Date: Wed, 10 Jun 2026 15:41:13 GMT +Connection: keep-alive +Keep-Alive: timeout=5 +``` --- From 30cd7c060b27eb7836a7a84ef4aebd6874121176 Mon Sep 17 00:00:00 2001 From: diffouo44 Date: Wed, 10 Jun 2026 19:35:13 +0300 Subject: [PATCH 10/10] feat(lab1): juice shop deploy + PR template + triage report --- submissions/lab1.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submissions/lab1.md b/submissions/lab1.md index 74a67ed93..21b28eb50 100644 --- a/submissions/lab1.md +++ b/submissions/lab1.md @@ -73,8 +73,8 @@ Keep-Alive: timeout=5 Which of these are MISSING? (cross-reference Lecture 1 OWASP Top 10:2025 — A06): - [x] `Content-Security-Policy` — missing - [x] `Strict-Transport-Security` — missing (site served over HTTP) -- [ ] `X-Content-Type-Options: nosniff` -- [ ] `X-Frame-Options` +- [ ] `X-Content-Type-Options: nosniff` — present +- [ ] `X-Frame-Options` — present ### Top 3 Risks Observed (2-3 sentences each)