SBOM: Software Bill of Materials
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.component | what 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 |
purl | the 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 |
licenses | the whole of license compliance, and a minimum element in CISA's revised guidance for exactly that reason |
dependencies | the 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.
Popular & reputable tools
| Tool | Notes |
|---|---|
| Syft | OSS, from Anchore; generates CycloneDX/SPDX from images and dirs |
| Trivy | OSS; can emit SBOMs as well as scan them |
| cdxgen | OSS; multi-ecosystem CycloneDX generator |
| SPDX tools / CycloneDX | The 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 counted | An 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 artifact | config.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