Skip to main content

All weeks · Worksheet · Overview · Supply-chain notes

Week 12 · Lecture slides

Week 12

Contents16 sections

Software Supply-Chain Security

Software Security · Nutthakorn Chalaemwongwan


Today

  • Why the supply chain is now top-tier
  • Dependency confusion & typosquatting
  • SBOMs, SLSA provenance
  • Signing with Sigstore/Cosign
  • 🎮 Game: Dependency Confusion Heist

The new #1 design risk

  • Your code is ~10% yours, ~90% dependencies
  • One bad package → thousands of victims (xz, event-stream, SolarWinds)
  • OWASP A03:2025 Software Supply Chain Failures

Real supply-chain attacks

CaseWhat happened
SolarWinds (2020)trojanized vendor update → 18k orgs
Log4Shell (2021)RCE in a ubiquitous logging dep
event-stream (npm)malicious dep stole crypto-wallet keys
XZ Utils (2024)backdoor planted in liblzma upstream
CircleCI (2023)stolen CI tokens → customer secrets

Attacks are shifting upstream: registry → maintainer → CI/CD.


Attack vectors

  • Typosquatting — reqeusts vs requests; the malicious package's setup.py runs at install time, before any of your code runs — you don't have to import it to be owned
  • Dependency confusion (CWE-1357) — public pkg shadows internal name
  • Malicious updates — compromised maintainer
  • Transitive risk — deps of deps you never chose

Try it — which package actually installs?

Set both versions and the index mode. The resolver's real rule decides.

Which package actually installs? Computed live (Week 12) — open full size

SCA — find vulnerable deps

docker run --rm -v "$PWD:/src" aquasec/trivy fs /src   # this lab's Python deps
pip-audit                                                          # vs PyPI advisories
  • Produces CVEs + fix versions
  • CWE-1104 (unmaintained), CWE-829 (untrusted inclusion), CWE-1395 (known-vulnerable 3rd-party dependency)

Integrity: prove what you shipped

  • SBOM (CycloneDX/SPDX) — ingredient list of the build
  • SLSA — levels of build provenance & tamper-resistance
  • A08:2025 Software/Data Integrity Failures

Signing with Cosign (keyless)

IMG=week12-supplychain:lab
docker run --rm -v "$PWD:/src" -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy:latest image --format cyclonedx \
  --output /src/sbom.cdx.json "$IMG"                          # SBOM
docker run --rm -e COSIGN_EXPERIMENTAL=1 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  gcr.io/projectsigstore/cosign:latest sign --yes "$IMG"       # sign (OIDC)
docker run --rm -e COSIGN_EXPERIMENTAL=1 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  gcr.io/projectsigstore/cosign:latest verify \
  --certificate-identity-regexp ".*" \
  --certificate-oidc-issuer-regexp ".*" "$IMG"                 # verify
  • Unsigned/tampered image → verification fails
  • Every step needs the docker-socket mount (trivy has to reach the local image, not just a local dir) — the SBOM step is a common copy-paste failure if it's dropped
  • cosign verify needs the two --certificate-*-regexp flags in keyless mode — without them it errors, it doesn't just "fail closed"
  • Keyless signing is backed by Fulcio (short-lived cert authority) + Rekor (public transparency log) — no long-lived private key sitting on disk to leak (CWE-321)

Tooling — GitHub Advanced Security (GHAS)

  • Secret scanning + push protection — block secrets at push time (before they reach the remote)
  • CodeQL code scanning — semantic SAST queries
  • Dependabot — alerts + auto-PRs for vulnerable deps
  • Native in the repo → results in the Security tab

Defenses

  • pip install --require-hashes — a substituted package's hash won't match; install refuses
  • Single --index-url, not --extra-index-url — one trusted index; the resolver won't "shop around" and pick a higher-versioned public package over your internal one
  • Verify signatures before deploy (admission policy)
  • Generate + store SBOMs per release
  • 2FA/MFA on dev/CI/cloud accounts; least privilege
  • Automate SCA in CI (next week)

The whole journey, one dependency

Six hops a dependency crosses from a public registry into production, each with its own attack and the control that answers it: registry (typosquatting → one trusted index), resolver (dependency confusion → scope the namespace), fetch (compromised maintainer → lockfile with hashes), build (stale pins → SCA in CI), image (no inventory → SBOM), deploy (unsigned image → Sigstore keyless gate). "We scanned our code" covers none of this — SAST reads the code you wrote, all six hops are code you didn't.


📦 Game — Dependency Confusion Heist

  1. Attack: no live registry in this lab — use the resolver simulation to watch a higher-version public pkg beat the private one
  2. Defend: pin + scope; generate SBOM; sign & verify with Cosign; add a provenance gate

Deliverable

📋 Worksheet 12 — labs/week12-supply-chain/worksheet.md (Parts 1–4) · kickoff: bash sca_scan.sh (trivy fs + pip-audit)

  • SCA report + remediation plan (Part 3)
  • SBOM file + sign/verify transcript (Part 3)
  • One-paragraph SLSA self-assessment + XZ Utils case analysis (Part 4)
  • + Audit the AI / EiPE / Prompt Problem (required, after Part 4 — see worksheet)

Key takeaways

  • Most of your attack surface is other people's code
  • Know your ingredients (SBOM), prove your build (SLSA), sign your artifacts
  • Verify before you trust

Questions?

Next week: Cloud & container security

All weeks in Software Security