← All guides

SBOM: Software Bill of Materials

Build & artifacts
On this page

In plain terms

An SBOM (Software Bill of Materials) is a complete list of ingredients for a piece of software: every component, library, and dependency it contains, with versions. Like the ingredient label on food, it doesn't do anything on its own, but it's the record that makes everything else possible, from vulnerability matching to license compliance.

Why it matters

When the next Log4Shell-scale vulnerability drops, the first question every organization scrambles to answer is: "are we affected, and where?" Without an SBOM, that's an archeology project across dozens of services. With one, it's a query. SBOMs became a mainstream requirement partly through the US Executive Order 14028 on supply-chain security, and they're now a common procurement ask.

Where it fits

  • Artifact: produced at build time for each release, from source, dependencies, and/or the final container image.
  • Stage: generate on build; store it as a release artifact; feed it into vulnerability scanning and share it with downstream consumers.

How it works

An SBOM generator inspects your project or image, enumerates every component and version, and writes it out in a standard format. The two dominant ones are CycloneDX and SPDX. That document can then be scanned for vulnerabilities (SCA and image scanning are "SBOM + vulnerability match") and archived so you can answer "what was in release 1.4.2?" months later.

What one actually looks like

The first honest thing to say is that a small one is large. A Node project declaring two dependencies produces a CycloneDX document listing 69 libraries in about 46 KB: two that somebody asked for and sixty-seven that came along. An SBOM is written to be queried rather than read, and the top level is short, with everything in the last three keys:

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.6",
  "serialNumber": "urn:uuid:e1d6659f-424e-...",
  "metadata":     { "timestamp": "...", "component": { } },
  "components":   [ ],
  "dependencies": [ ]
}

One of those 69 entries, in full:

{
  "bom-ref": "pkg:npm/lodash@4.17.20",
  "type": "library",
  "name": "lodash",
  "version": "4.17.20",
  "purl": "pkg:npm/lodash@4.17.20",
  "licenses": [ { "license": { "id": "MIT" } } ]
}

Four things in there carry the weight:

What rests on it
metadata.componentwhat the document is about. Without it you hold a list of packages and nothing saying which artifact contains them, which is a pile rather than an inventory
purlthe join key. pkg:npm/lodash@4.17.20 is what a scanner matches against a vulnerability database, what a VEX statement names when it says a flaw does not affect you, and what a second tool has to spell identically for the two to be discussing the same package
licensesthe whole of license compliance, and a minimum element in CISA's revised guidance for exactly that reason
dependenciesthe edges. Which package pulled this one in is nowhere in components, and it decides whether the fix is "upgrade it" or "wait for whoever depends on it"

The purl is the one to remember. Vulnerability matching, VEX, a query across every release you kept: each is a string comparison against that field, and each fails silently when two tools spell the same package differently.

The same question, asked of a search and asked of a record A vulnerability is published and every organization asks whether it is affected. Without a stored inventory the answer is reconstructed service by service, from lockfiles that may not exist and images whose contents nobody recorded, and it is out of date before the last service is checked. With one inventory document kept per release, the same question is one query, and it still answers for releases that are running in production but are no longer being built. A VULNERABILITY IS PUBLISHED. ARE YOU AFFECTED, AND WHERE? Nothing was written down open each service in turn find the lockfile, where there is one work out what the base image added ask whoever built the one nobody owns Days of work across several teams, and still no way to say you checked everywhere. One document per release one query across everything you kept api 2.4.0 affected worker 1.9.2 affected gateway 3.1.0 not present Including the releases still running that nobody is building any more. An inventory finds nothing and fixes nothing. It is what makes the question answerable at the speed it gets asked.
The last row on the right is the one that is hard to reconstruct any other way: a release that is in production and out of the build pipeline has no lockfile to go back to.
ToolNotes
SyftOSS, from Anchore; generates CycloneDX/SPDX from images and dirs
TrivyOSS; can emit SBOMs as well as scan them
cdxgenOSS; multi-ecosystem CycloneDX generator
SPDX tools / CycloneDXThe two standard formats

Common pitfalls

  • An SBOM you never use is just a file. Its value comes from feeding it into scanning and incident response, not from generating it.
  • Accuracy varies by depth. Source-based and image-based SBOMs capture different things; know what yours covers.
  • Keep it with the release. An SBOM must correspond to the exact artifact you shipped, generate and store it per release, ideally signed.

How Draugr fits. SBOM generation ships today, backed by Syft. Set config.sbom.enabled: true and every scan also inventories each repository and image, CycloneDX JSON by default, SPDX JSON where a procurement or license process asks for ISO/IEC 5962 by name. Documents are written alongside your other artifacts and delivered by any publisher you have configured. See the Saga reference.

Reported, never countedAn SBOM is not a control. Every control checks something and returns a verdict; an inventory finds nothing, so it has none to give, and a row that always reads pass without having looked is worse than no row. It travels as evidence.
One document, or one per artifactconfig.sbom.scope: project assembles a single document for the release rather than one per repository and image, which is the unit an SBOM is usually asked for in.
The assessment beside it--report vex emits the VEX document saying which of the vulnerabilities in the inventory actually affect you.

Draugr's own release pipeline publishes signed SBOMs for its binaries.

Keep learning

  • SCA, scanning the components an SBOM lists
  • Container image scanning, SBOM + vulnerability match for images
  • VEX, saying which of the vulnerabilities in an SBOM affect you