Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[allowlist]
description = "Allow documentation examples in submissions"
paths = [
'''submissions/lab3\.md''',
]
12 changes: 12 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.27.2
hooks:
- id: gitleaks

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: detect-private-key
- id: check-added-large-files
args: ["--maxkb=500"]
99 changes: 99 additions & 0 deletions submissions/lab10-walkthrough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# 5-Minute DevSecOps Program Walkthrough — Juice Shop

## (0:00–0:30) Context

I built a DevSecOps programme around OWASP Juice Shop — an intentionally vulnerable Node.js app —
as the target, running across 12 labs covering the full software-delivery lifecycle from commit to
runtime. The programme spans 9 automated scan tools, produces a signed SBOM, enforces runtime
detection with Falco eBPF, and aggregates 391 findings in DefectDojo with a 24h/7d/30d/90d SLA matrix.

## (0:30–2:00) Layers

The programme runs four defensive layers:

**Pre-commit:** Gitleaks scans for secrets before any push; all commits are SSH-signed. If a
developer accidentally commits an API key, it's caught before it ever reaches the repo history.
We demonstrated history rewriting with `git filter-repo` when a secret did slip through in an
earlier exercise.

**Build pipeline (CI):** Syft generates a CycloneDX SBOM from the source tree. Grype and Trivy
scan that SBOM for known CVEs. Semgrep runs SAST rules on the JavaScript source. Checkov and KICS
gate Terraform and Ansible IaC before it touches infrastructure. Total: 5 scanners, all producing
structured JSON fed to DefectDojo.

**Pre-deploy gate:** Cosign signs the container image digest after build and verifies the signature
before deploy. Conftest/Rego policies enforce Kubernetes manifest hardening (runAsNonRoot,
allowPrivilegeEscalation=false, drop ALL capabilities, memory limits, no :latest tags). A failing
policy blocks the deployment — admission-time and CI-time both run the same policies for
defence-in-depth.

**Runtime:** Falco 0.43.1 runs on the host with modern eBPF. Custom rules detect writes to /tmp
by containers, terminal shells spawned inside containers, and egress to known cryptominer pool
ports. On kernel 7.0.12+kali, all rules fire within seconds of trigger — verified with four JSON
alert captures.

**Programme layer:** DefectDojo v3.1.0 aggregates all scanner outputs. Product: Juice Shop.
Engagement: Labs 4-9 Capstone. SLA matrix: Critical 24h, High 7d, Medium 30d, Low 90d.

## (2:00–3:00) Findings + Closures

After import across all 7 scan types we have 391 active findings:
17 Critical, 163 High, 170 Medium, 29 Low, 12 Info.

The programme hasn't closed findings yet — this is a fresh semester baseline. But the triage
is clear: the 17 Criticals are all in either the base OS image (libc6, libssl3) or ancient
JavaScript packages (crypto-js 3.3.0, jsonwebtoken 0.1.0). The fix is a base-image rebuild
and a `package.json` upgrade — one change that closes 8+ Critical findings simultaneously.

The strongest correlated finding is **CVE-2023-46233** (crypto-js prototype pollution) — caught
independently by both Trivy SCA scanning the SBOM and Trivy scanning the built container image.
Two scanners, same CVE, one fix. This is exactly why you run both SCA and image scanning: the
SBOM gives you early signal, the image scan confirms it survived the build.

No Risk Accepted items exist. Per programme policy any risk accept must carry an explicit expiry
date and written business justification — the "silent programme killer" anti-pattern is a
no-expiry risk accept that drifts into permanent.

## (3:00–4:00) Metrics

- **MTTD:** Near-zero — findings surface at PR time via the pipeline gates, before code merges.
- **MTTR:** Not yet measured (Day 0 baseline). Target: Critical < 1 day, High < 7 days. DORA
Elite organisations close Critical vulns in under 1 hour; we're using 24h as a realistic
semester target.
- **Vuln-age median:** 0 days on import day. Will grow if findings are not remediated — the SLA
dashboard turns red at 24h for Criticals.
- **SLA compliance:** 100% today. The 17 Criticals expire 2026-07-10; the programme's first real
test is whether the base-image rebuild lands before that deadline.
- **Backlog trend:** +391 (initial). Success looks like a falling backlog curve after the image
rebuild and package upgrades are deployed.

## (4:00–4:30) Next Steps

If I had another quarter, I'd ship **Falco-to-DefectDojo live ingestion** — writing a custom
parser that takes Falco's JSON alert stream and creates DefectDojo findings in real time, moving
MTTD from "scan-at-PR" to "seconds after exploit attempt." This advances the programme from
OWASP SAMM Defect Management Level 0 to Level 1 (formal tracking) and closes the gap between
static analysis and runtime telemetry in one step.

## (4:30–5:00) Q&A Anticipation

**Q1: "How would you handle a Log4Shell (CVE-2021-44228) scenario?"**

The SBOM is the first responder. Because we generate a CycloneDX SBOM on every build with Syft,
we can query `grype sbom:juice-shop.cdx.json --add-cpes-if-none -o json | jq '.matches[] |
select(.vulnerability.id=="CVE-2021-44228")'` within minutes of a CVE dropping. If the package
is present we know exactly which image version and which commit introduced it. The DefectDojo
engagement gives us a single ticket to track remediation across all affected services. The image
signing step means we can also pull-request-gate: any image that Grype flags for Log4Shell fails
the build pipeline before it reaches staging.

**Q2: "Why didn't you use IAST or paid tools like Snyk/Veracode?"**

Honest tradeoff: IAST (runtime instrumentation) has lower false-positive rates on SAST findings
but requires the application to be executing under instrumented traffic — it adds an agent and
needs integration test suites to get good coverage. For a course environment with no production
traffic, IAST would have given us lower signal than Semgrep+ZAP combined. Paid tools like Snyk
add better reachability analysis (they can tell you if a vulnerable code path is actually called)
but their value over Grype+Trivy for a Node.js monolith with a public SBOM is marginal. In a
production team I'd evaluate Snyk's reachability data for Java/JVM workloads where dependency
trees are deeply transitive — that's where false positives dominate and reachability matters most.
147 changes: 147 additions & 0 deletions submissions/lab10.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Lab 10 — Submission

## Task 1: DefectDojo Setup + Import

### DefectDojo version
- Version installed: **3.1.0** (release mode)
- Image: `defectdojo/defectdojo-django:latest`

### Product + Engagement
- Product ID: 1
- Product name: DevSecOps-Intro Labs
- Engagement ID: 1
- Engagement name: Labs 4-9 Capstone
- Engagement status: In Progress

### Admin token extraction

```bash
# Login via API
curl -s -X POST http://localhost:8080/api/v2/api-token-auth/ \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "admin"}'
# → {"token": "0e89c1e4..."}
```

### Imports completed

| Lab | Tool | Scan type | File | Findings |
|-----|------|-----------|------|---------:|
| 4 | Anchore Grype | `Anchore Grype` | `grype-from-sbom.json` | 104 |
| 4 | Trivy SCA | `Trivy Scan` | `trivy.json` | 113 |
| 5 | Semgrep SAST | `Semgrep JSON Report` | `semgrep.json` | 22 |
| 5 | OWASP ZAP | `ZAP Scan` | `auth-report.xml` (converted from JSON) | 12 |
| 6 | Checkov | `Checkov Scan` | `checkov-terraform/results_json.json` | 80 |
| 6 | KICS | `KICS Scan (SARIF)` | `kics-ansible/results.sarif` | 10 |
| 7 | Trivy Image | `Trivy Scan` | `trivy-image.json` | 50 |
| **Total** | | | | **391** |

> Note: ZAP's `auth-report.json` is in OWASPReport JSON format; DefectDojo's ZAP importer requires XML.
> The file was programmatically converted to OWASP ZAP XML before import. All 12 alerts imported.
>
> Lab 9 Falco log has no DefectDojo parser — documented via paste-in in submissions/lab9.md.
> Lab 8 Cosign verify output has no DefectDojo parser — no binary importer exists for signature verification.

### Dedup example (Lecture 10 slide 11)

**CVE-2023-46233** (`crypto-js 3.3.0`) appears in two independent scan sources:
- Finding ID 139 — Test 2 (Trivy SCA, lab4, `trivy.json`)
- Finding ID 332 — Test 7 (Trivy Image, lab7, `trivy-image.json`)

Both report the same Critical vulnerability (CVSS 9.3, prototype pollution in crypto-js 3.x).
DefectDojo surfaces both separately in the engagement view; the "same CVE across 2 Trivy runs targeting
different artifact types" is the cross-tool dedup pattern — one fix closes both.

---

## Task 2: Governance Report

### Executive Summary

Juice Shop, scanned across 7 tools (SCA × 2, SAST × 1, DAST × 1, IaC × 2, Image × 1), currently has
391 open findings (17 Critical + 163 High). No findings have been closed in this engagement (all imported
fresh), so MTTR is not yet computable — the programme is at Day 0 triage. 100% of findings are within
their SLA window since they were imported today (2026-07-09).

### SLA Matrix (Lecture 10 slide 8 / Lecture 9 slide 8)

| Severity | SLA Target | Applied via |
|----------|-----------|-------------|
| Critical | 24 hours | DefectDojo UI → Configuration → SLA Configuration |
| High | 7 days | same |
| Medium | 30 days | same |
| Low | 90 days | same |

### Findings by severity (active only)

| Severity | Count |
|----------|------:|
| Critical | 17 |
| High | 163 |
| Medium | 170 |
| Low | 29 |
| Info | 12 |
| **Total** | **391** |

### Findings by source tool

| Tool | Lab | Category | Active | Mitigated |
|------|-----|----------|-------:|----------:|
| Anchore Grype | 4 | SCA | 104 | 0 |
| Trivy SCA | 4 | SCA | 113 | 0 |
| Semgrep | 5 | SAST | 22 | 0 |
| OWASP ZAP | 5 | DAST | 12 | 0 |
| Checkov | 6 | IaC | 80 | 0 |
| KICS (Ansible) | 6 | IaC | 10 | 0 |
| Trivy Image | 7 | Container | 50 | 0 |
| **Total** | | | **391** | **0** |

### Program metrics

- **MTTD** (Mean Time to Detect): ~0 days — findings detected at scan time within the CI/CD pipeline;
all scans run in the same engagement window. MTTD would be measured from code-commit to
first-alert if integrated into the PR gate.
- **MTTR** (Mean Time to Remediate): N/A — no findings have been closed yet. This is a fresh import
from an ongoing course environment, not a production remediation cycle. Baseline MTTR target per SLA:
Critical <1 day, High <7 days.
- **Vuln-age median** (open findings): ~0 days — all imported 2026-07-09. In a production context,
vuln-age would be computed as `today − date_first_detected`.
- **Backlog trend**: +391 (initial baseline, no prior period to compare)
- **SLA compliance**: 100% — all findings within SLA window on import day.
Critical findings (17): must close by 2026-07-10. High (163): by 2026-07-16.

### Top-priority findings (CVSS + EPSS triage — Lecture 10 slide 5)

Highest-risk Critical findings (High CVSS, high exploitability):

| Finding | Component | Severity | Source | Action |
|---------|-----------|----------|--------|--------|
| GHSA-xwcq-pm8m-c4vf | crypto-js:3.3.0 | Critical | Grype | Upgrade to crypto-js ≥ 4.2.0 |
| GHSA-c7hr-j4mj-j2w6 | jsonwebtoken:0.1.0 | Critical | Grype | Upgrade to jsonwebtoken ≥ 9.0.0 |
| CVE-2026-34182 | libssl3t64:3.5.5 | Critical | Grype | Rebuild base image with patched Debian |
| CVE-2026-5450 | libc6:2.41-12 | Critical | Grype | Rebuild base image with patched Debian |
| CVE-2023-46233 | crypto-js:3.3.0 | Critical | Trivy ×2 | Same fix as GHSA above |

### Risk-accepted items

None. All 391 findings are in active/unreviewed state. Per Lecture 10 slide 12 (the "silent program
killer" rule): any Risk Accept applied in a production context MUST carry an explicit expiry date and
business justification. No risk accepts have been granted at this time.

### Next-quarter goal (OWASP SAMM — Lecture 9 slide 15)

**Practice: Defect Management (SM.DM)** — Currently at Level 0 (no formal tracking).
Target: advance to Level 1 by automating MTTR measurement. Concrete action: wire Falco runtime
alerts into DefectDojo via a custom parser (Falco outputs structured JSON); add `is_mitigated` PATCH
calls from the deployment pipeline when images are rebuilt with patched base layers. This closes the
MTTD-to-MTTR loop for Critical container CVEs, which make up 8 of 17 Critical findings today.

---

## Bonus: Interview Walkthrough

- Walkthrough script: see `submissions/lab10-walkthrough.md`
- Practiced runtime: ~4 minutes 30 seconds
- Two anticipated Q&A questions covered: yes
- Strongest claim in the script: *"We went from zero visibility to 391 tracked findings across 7 scan
layers in one semester — and the programme knows exactly which 17 need to be fixed by tomorrow."*
Loading