SBOMs and Supply-Chain Security, Practically
Introduction¶
Software supply-chain attacks compromise you through a dependency, a build tool, or a compromised package — not your own code. The defensive toolkit is SBOMs (know what you ship), provenance (know how it was built), and pinning (know it won't change under you).
SBOM — the ingredients list¶
A Software Bill of Materials lists every component in an artifact: direct and transitive dependencies, versions, licenses, and hashes. Two formats:
- SPDX — ISO standard, license-focused origins, broad tooling.
- CycloneDX — OWASP, security-focused, richer vulnerability/VEX support.
Generate one with syft:
syft dir:. -o spdx-json=sbom.spdx.json
syft ghcr.io/org/app:1.4.2 -o cyclonedx-json=sbom.cdx.json
Generate at build time and attach it to the image as a signed attestation (see signing images with cosign).
Scan the SBOM¶
grype sbom:sbom.spdx.json --fail-on high
grype ghcr.io/org/app:1.4.2 -o table
# Trivy does both SBOM generation and scanning
trivy image --format cyclonedx --output sbom.json ghcr.io/org/app:1.4.2
trivy sbom sbom.json
Run in CI on every build; fail the build on new criticals. Re-scan stored SBOMs of already-released images on a schedule — a CVE disclosed today affects an image you built and shipped last month, and only an SBOM scan tells you which ones.
VEX (Vulnerability Exploitability eXchange) documents let you record "CVE-X is present but not exploitable in our usage" so the scanner stops flagging it, with an auditable reason.
Provenance — SLSA¶
SLSA (Supply-chain Levels for Software Artifacts) is a framework for build integrity. The levels, roughly:
- L1 — provenance exists (you can see how it was built).
- L2 — provenance is signed, build runs on a hosted service.
- L3 — the build is isolated, non-falsifiable provenance, ephemeral environment. GitHub Actions with the SLSA reusable workflow, or a properly configured Tekton/Argo, reaches L3.
Provenance answers: which source commit, which builder, which parameters, produced this exact digest. Verify it at deploy time.
# SLSA GitHub generator produces a signed provenance attestation
cosign verify-attestation --type slsaprovenance \
--certificate-identity-regexp '^https://github.com/slsa-framework/.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/org/app@sha256:...
The controls that actually matter¶
Ranked by risk reduction per effort:
- Lockfiles + reproducible installs.
--frozen/npm ci/go.sum. A floating range means a compromised patch release lands silently. - Pin GitHub Actions to a full SHA, not a tag (
uses: actions/checkout@<sha>). Tags are mutable; a compromised action tag = code execution in your CI with your secrets. - Minimal, verified base images — distroless / slim, from a source you trust, pinned by digest.
- Scan on build AND rescan stored SBOMs for released artifacts.
- Least-privilege CI — OIDC not long-lived keys, scoped tokens, no secrets on forked-PR runs (see secrets in CI).
- Sign artifacts, verify at admission.
- Dependency review automation (Dependabot/Renovate) with tested update PRs.
Generating an SBOM and never scanning it is compliance theatre. The SBOM is input to a process, not the deliverable.
Verification and troubleshooting¶
syft version; grype version
grype db status # is the vuln DB current?
grype ghcr.io/org/app:1.4.2 --only-fixed # ignore CVEs with no fix available
- Scanner reports hundreds of CVEs on a base image — mostly OS packages with
no fix, or low severity.
--only-fixed, set a severity threshold, use a smaller base (distroless drops most), and VEX the ones you've assessed. - SBOM missing dependencies — syft scans what's in the artifact; a multi-stage build that copies only a binary loses the build-time deps (that's good for the runtime SBOM, but generate one at the build stage too if you need full coverage).
- Different scanners disagree — different vuln databases and matching logic. Pick one as authoritative for gating, note the others.
- CI green, but a released image is now vulnerable — you didn't rescan.
Automate a nightly
grypeover your published digests. - Provenance verification fails for a reusable workflow — the identity is the reusable workflow's path, not your repo's. Adjust the identity regex.
grype dbfails to update in an airgapped build — host the DB internally (grype db updatethen serve the archive), setGRYPE_DB_UPDATE_URL.
Related tools and reading¶
- On-site: JSON formatter.
- Related posts: Signing container images with cosign, Handling secrets in CI without leaking them.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in DevOps & CI/CD
OpenTelemetry for Distributed Tracing
A trace follows one request across services. OpenTelemetry is the vendor-neutral way to produce them — here's the model, the Collector, and sampling that won't bankrupt you.
October 24, 2026pre-commit Hooks That Actually Help
pre-commit runs checks before a commit lands, catching formatting, secrets, and syntax errors locally. Here's a config that helps without being annoying.
October 24, 2026Chaos Engineering Basics
Chaos engineering is controlled experiments that inject failure to verify your system tolerates it. Here's the method, safe blast radius, starter experiments, and game days.
October 24, 2026