Server

SBOMs and Supply-Chain Security, Practically

By Prabath Thalangama· October 24, 2026· 4 min read
#supply-chain#security#sbom

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:

  1. Lockfiles + reproducible installs. --frozen/npm ci/go.sum. A floating range means a compromised patch release lands silently.
  2. 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.
  3. Minimal, verified base images — distroless / slim, from a source you trust, pinned by digest.
  4. Scan on build AND rescan stored SBOMs for released artifacts.
  5. Least-privilege CI — OIDC not long-lived keys, scoped tokens, no secrets on forked-PR runs (see secrets in CI).
  6. Sign artifacts, verify at admission.
  7. 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 grype over 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 db fails to update in an airgapped build — host the DB internally (grype db update then serve the archive), set GRYPE_DB_UPDATE_URL.
PrabathStuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.