SBOM and supply chain
A new dependency CVE drops and the first question is "are we affected?" An SBOM — the per-build inventory of everything you ship — is what lets you answer in minutes instead of days. Provenance and SLSA add the missing half: trusting that inventory.
December 2021, late afternoon: a CVE lands for Log4j 2 — CVE-2021-44228, a remote-code-execution bug reachable by a single crafted log line, scored 10.0. Every security channel lights up. Your CTO asks the one question that matters: are we affected? And the room goes quiet. Nobody can say. Log4j is not in your package.json or your pom.xml top level — it’s a transitive dependency four layers deep, pulled in by a logging adapter that a framework needed. It might be in three services or thirty. The teams that answered in twenty minutes had a Software Bill of Materials: a queryable, per-build list of every component they shipped. The teams that answered in nine days grepped source repos by hand. Same bug, same patch — the gap was entirely about whether they knew what they were running.
By the end of this lesson you’ll know what an SBOM actually contains, why “are we affected?” is unanswerable without one, and why knowing your dependencies is only half the job — the other half is trusting them.
What an SBOM actually is
A Software Bill of Materials is a formal, machine-readable inventory of every component in a piece of software — direct dependencies, transitive dependencies, and often the OS packages baked into your container image. The mental model is the ingredient label on food: not marketing copy, but the literal list of what’s inside, in a format a machine can parse and a regulator can audit. Two standards dominate in practice: SPDX (an ISO standard, ISO/IEC 5962) and CycloneDX (from OWASP, built security-first). Both express the same core facts — component name, version, and a unique identifier called a purl (package URL, e.g. pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1) — plus a cryptographic hash so you can prove the artifact wasn’t swapped.
The single property that makes an SBOM worth generating is that it captures the transitive graph, not just the top level. When you npm install express, you pull in dozens of packages you never named; a real application’s dependency tree routinely runs to several hundred or a thousand nodes. The vulnerabilities that hurt almost always live in that buried majority — Log4j was four levels deep — which is exactly where a human reading package.json cannot see them. An SBOM flattens that whole graph into a list you can query: “does any version of log4j-core appear anywhere in this build?” becomes a one-line lookup instead of a multi-day archaeology dig.
The non-obvious rule: an SBOM is generated at build time, per artifact, not written by hand. A hand-maintained dependency list is stale the moment someone bumps a lockfile, and stale is worse than nothing because it lies with confidence. The SBOM is an output of the build, pinned to a specific image digest, so it describes exactly the bytes you deployed — not what the README claims you deployed.
Why “are we affected?” is the whole point
An SBOM has no value sitting in a bucket. Its value is realized when a new vulnerability is published and you feed it into a scanner that matches each component’s purl against a CVE feed. This is the difference between inventory and detection: the inventory tells you what you have; the scan tells you which of it is now known to be dangerous. Because new CVEs are published every day against components you froze months ago, the match has to be continuous — a vulnerability that was clean at build time becomes critical the day its CVE drops, and your stored SBOM is what lets you reach back and find it without rebuilding or re-grepping anything.
This is also where prioritization enters, because a real scan against a thousand-node tree returns dozens of hits and you cannot patch them all this week. CVSS — the Common Vulnerability Scoring System — gives each CVE a 0–10 base score, a vendor-neutral severity that’s comparable across teams. But the base score is deliberately worst-case and context-free: it assumes the vulnerable code is reachable and the asset is exposed. The senior move is to layer CVSS’s environmental and temporal metrics — is this component actually on a reachable path? is the asset internet-facing? does an exploit exist in the wild? — on top of the base score. A 9.8 base CVE in a dev-only build tool you never ship is lower real risk than a 7.5 in your internet-facing auth service. The SBOM tells you where the component is, which is precisely the context that turns a raw score into a real priority.
▸Why this works
Why isn’t grepping your repos “good enough” instead of generating an SBOM? Because source ≠ artifact. Your repo shows what you wrote; the SBOM shows what you shipped. A transitive dependency resolved by your lockfile, a base-image OS package, a vendored binary, a version pinned differently in CI than locally — none of those are visible from grep over your source tree, and all of them ship in the container. The Log4j teams who grepped found their direct usages and missed the framework adapter that pulled it in transitively. The SBOM is generated from the resolved build, so it sees the bytes, not the intentions.
The other half: provenance and trust
Knowing what you ship is only half the supply-chain problem. The other half is trusting that the component you pulled is the one its author actually published — and that question got urgent after attacks like SolarWinds (2020), where malicious code was injected into the build pipeline so the signed, official artifact itself was poisoned, and Codecov and the event-stream npm hijack, where a legitimate package was taken over and a malicious version pushed. In every case the dependency name and version looked correct. An SBOM would have told you that you were running that package; it would not have told you the package had been tampered with upstream. That gap is what provenance closes.
Provenance is verifiable, tamper-evident metadata answering where did this artifact come from, from which source commit, built by which pipeline? — cryptographically signed so it can’t be forged after the fact. SLSA (Supply-chain Levels for Software Artifacts, pronounced “salsa”) is the framework that grades how trustworthy that provenance is, in levels: roughly, L1 means provenance exists, L2 means it’s signed by the build service, and L3 means the build ran in a hardened, isolated environment that the producer can’t tamper with. The combined posture is the goal: an SBOM so you know your components, provenance so you trust they’re authentic, and a patch SLA — like the timelines NIST SP 800-40 prescribes — so a known-bad component gets remediated on a clock instead of whenever someone notices.
A critical RCE CVE just dropped for a popular logging library. Your CTO asks: 'Are we affected, and where?' Which capability actually answers that in minutes?
Why must an SBOM be generated at build time per artifact rather than maintained as a hand-written document?
An attacker compromises a build pipeline and ships a poisoned version of an official, correctly-named package (SolarWinds-style). Which control would have caught this, and which would not?
Order the supply-chain workflow from build to answering 'are we affected?':
- 1 Build resolves all dependencies (direct + transitive + base image)
- 2 Generate the SBOM per artifact, pinned to the image digest
- 3 Store the SBOM alongside the deployed artifact
- 4 Continuously match each purl against fresh CVE feeds
- 5 On a new CVE, query the SBOM and prioritize hits with CVSS + reachability
- 01What exactly is in an SBOM, and why does generating it at build time per artifact matter?
- 02Knowing your dependencies is only half the supply-chain problem — what's the other half, and what closes it?
An SBOM is a formal, machine-readable inventory of every component you ship — direct, transitive, and base-image — expressed in SPDX or CycloneDX with a purl and a hash per component. Its whole reason to exist is the question a fresh CVE forces: “are we affected, and where?” Because the dangerous components are almost always buried deep in the transitive graph (Log4j was four levels down), a manifest read or a source grep gives a confident-but-wrong answer, while a build-time SBOM that’s continuously scanned against CVE feeds answers it in minutes — then CVSS base scores plus reachability and exposure context turn the dozens of hits into a real patch priority. But inventory is not trust: SolarWinds-style attacks poison correctly-named official artifacts, which an SBOM alone can’t catch, so you pair it with provenance and SLSA to prove components are authentic and a patch SLA to remediate the bad ones on a clock. Now when a critical dependency CVE drops, your first move isn’t to grep — it’s to query the SBOM and ask which of those hits is actually reachable and exposed.
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.