🔎 Last updated · Editorial policy · Report an error
TL;DR: What Is an SBOM and Why Does It Matter Now?
An SBOM—Software Bill of Materials—is a machine-readable inventory of every component, library, dependency, and license inside a piece of software. Think ingredients label, but for binaries. After SolarWinds, Log4Shell, and a wave of supply-chain regulation starting with Executive Order 14028, producing and consuming SBOMs stopped being a maturity-project nice-to-have and became table stakes. If you ship software—or buy it—you need one.
Why SBOMs Became Non-Negotiable: SolarWinds, Log4Shell, and the Executive Order
The traditional software acquisition playbook assumed you could trust your vendors. The SolarWinds SUNBURST campaign of December 2020 shredded that assumption: attackers compromised the build pipeline itself and pushed trojanized updates to roughly 18,000 customers. You cannot defend against what you cannot see—and nobody, including SolarWinds, had a complete component inventory of their own product.
Then Log4Shell (CVE-2021-44228) landed in December 2021 and made the cost of that blindness concrete. Organizations spent weeks crawling spreadsheets and running ad-hoc greps across codebases to answer one question: do we run a vulnerable version of Log4j? Teams with accurate SBOMs answered it in minutes.
Regulators noticed. In May 2021, Executive Order 14028 directed the U.S. federal government to improve software supply-chain security, explicitly calling for SBOMs. In September 2022, OMB memorandum M-22-18 made it mandatory: any software producer selling to federal agencies must provide SBOMs following the NTIA minimum elements. CISA has since built an entire SBOM practice area around it—see cisa.gov/sbom—and the EU Cyber Resilience Act pushes the same direction globally. The mandate is real, and it’s expanding.
SBOM Fundamentals: What Goes Into a Bill of Materials
An SBOM is not a text file of library names. The NTIA minimum elements define the baseline every valid SBOM must carry:
- Supplier name — who produced the component
- Component name — the canonical package or library name
- Version — the exact version string
- Unique identifiers — hashes, PURLs (Package URLs), or SPDX IDs
- Dependency relationships — which components are upstream/downstream of others
- SBOM author — who generated the document and when
Richer SBOMs add license identifiers (critical for compliance teams), file-level hashes, and completeness metadata. The dependency graph is the part most people underappreciate—knowing that your application pulls lodash@4.17.20 matters less than knowing how it got there, because that’s what tells you what to patch or pin when the next transitive-dependency CVE drops.
SPDX vs CycloneDX: The Two Formats Compared
Two formats dominate, and both are standards—this is not a vendor lock-in fight. The Linux Foundation’s SPDX (Software Package Data Exchange) is an ISO standard (ISO/IEC 5962:2021) that grew out of license compliance work. CycloneDX originated at OWASP with security operations as its design center.
| Dimension | SPDX | CycloneDX |
|---|---|---|
| Origin / steward | Linux Foundation, ISO/IEC 5962:2021 | OWASP, now OWASP Foundation |
| Original focus | License and compliance | Security and risk |
| Native vulnerability support | Bolted on (external refs, VEX profiles) | First-class: CVSS, CWE, vulnerability objects in-spec |
| Serializations | JSON, tag-value, RDF, XML, YAML, spreadsheets | JSON, XML, protobuf |
| Tooling ecosystem | Strong in compliance/scanning tools | Strong in security tooling (Syft, Grype, Trivy, Dependency-Track) |
| Pick it when | Licensing, procurement, regulatory compliance | Vulnerability management, incident response, SBOM-as-input-to-scanning |
The practical answer for most security teams: produce both, automate both, treat CycloneDX as your operational format and SPDX as your compliance export. You’re attacking a metadata problem—wrapped in procurement logic—so don’t let format debates delay adoption.
SPDX in Practice: Structure, Formats, and Example Snippets
SPDX documents describe packages with unique SPDX IDs (SPDXRef-...) and link them via relationship entries. A trimmed SPDX JSON excerpt:
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "myapp-1.0.0",
"creationInfo": {
"created": "2024-11-03T10:15:00Z",
"creators": ["Tool: syft-1.18.0"]
},
"packages": [
{
"SPDXID": "SPDXRef-Package-log4j-core",
"name": "log4j-core",
"versionInfo": "2.14.1",
"licenseConcluded": "Apache-2.0",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1"
}
]
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relationshipType": "DESCRIBES",
"relatedSpdxElement": "SPDXRef-Package-myapp"
},
{
"spdxElementId": "SPDXRef-Package-myapp",
"relationshipType": "DEPENDS_ON",
"relatedSpdxElement": "SPDXRef-Package-log4j-core"
}
]
}
The tag-value format is the classic SPDX serialization—SPDXVersion: SPDX-2.3, PackageName: log4j-core, one key-value pair per line—and it’s still what some legacy compliance tooling expects.
CycloneDX in Practice: Structure and Example Snippets
CycloneDX centers on bom-ref identifiers and Package URLs (purl) for unambiguous component identification—and, unlike SPDX, vulnerabilities live in the spec itself:
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:1b6f1a7e-8f9c-4d2a-9f3b-7c2e1a4b5d6f",
"version": 1,
"metadata": {
"tools": [{"vendor": "Anchore", "name": "syft", "version": "1.18.0"}]
},
"components": [
{
"bom-ref": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1",
"type": "library",
"name": "log4j-core",
"version": "2.14.1",
"purl": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1",
"licenses": [{"license": {"id": "Apache-2.0"}}]
}
],
"vulnerabilities": [
{
"id": "CVE-2021-44228",
"source": {"name": "NVD"},
"ratings": [{"severity": "critical", "score": 10.0}],
"affects": [{"ref": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1"}]
}
]
}
That vulnerability block is the whole reason security teams lean CycloneDX: SBOM and vuln data in one document, consumable by tools like OWASP Dependency-Track.
Generating an SBOM with Syft: Hands-On Walkthrough
Syft from Anchore is the de facto open-source CLI for SBOM generation. It catalogs container images, filesystems, archives, and lockfiles. Install and run:
# Install
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
# Container image (remote, no Docker required)
syft nginx:1.25 -o cyclonedx-json > sbom.cdx.json
syft nginx:1.25 -o spdx-json > sbom.spdx.json
# Human-readable output
syft nginx:1.25
# A repository directory
syft dir:./my-app -o cyclonedx-json
# Just a lockfile
syft path:./package-lock.json -o cyclonedx-json
# Include file-level hashes (bigger, slower, more complete)
syft nginx:1.25 --scope all-layers -o cyclonedx-json
Typical output enumerates OS packages (dpkg, apk, rpm), language packages (npm, pip, Maven, Go modules), and their versions—hundreds of rows for a trivially small image. That count is the point: your “simple” Alpine-based container carries far more attack surface than its Dockerfile suggests.
From SBOM to Action: VulnOps with Grype
An SBOM you generate and never query is shelfware. Grype consumes Syft’s output and matches it against vulnerability feeds (NVD, GitHub Security Advisories, OS distro trackers):
# Scan an image directly (Syft under the hood)
grype nginx:1.25
# Or use your stored SBOM as the input
grype sbom:sbom.cdx.json
# Filter to high and critical
grype sbom:sbom.cdx.json --only-fixed --fail-on high
# Suppress accepted findings
grype sbom:sbom.cdx.json -o json --file report.json
grype sbom:sbom.cdx.json --ignore-query "artifact.name=openssl,cve=CVE-2023-0286"
Each finding carries severity, CVSS score, and fix availability. The --fail-on flag plus an allowlist file turns this into a CI gate: block merges that introduce critical CVEs, suppress vetted-and-unfixable findings with documented rationale, and re-scan on every build. Wire it in as a pipeline step and you’ve converted a point-in-time artifact into a continuous control.
Cutting the Noise with VEX: Machine-Readable Vulnerability Exploitability Statements
Raw vulnerability matching drowns you in false positives—findings for vulnerable code paths you never execute. VEX (Vulnerability Exploitability eXchange) fixes this with machine-readable statements about whether each CVE actually affects your product. Every VEX statement carries one of four impact statuses:
- not_affected — the vulnerable component is present but not exploitable (with justification)
- affected — yes, you need to act
- fixed — it was vulnerable, patched in this version
- under_investigation — analysis in progress
Two formats matter: CISA’s VEX work built on CSAF (the OASIS Common Security Advisory Framework), and OpenVEX, a lightweight, minimal spec purpose-built for SBOM integration. Both CycloneDX and SPDX now support VEX natively—CycloneDX 1.4+ via status vocabularies, SPDX 3.0 via its security profile. A not_affected statement with a valid justification is the difference between 400 findings and 12 actionable ones.
SBOMs for Blue Teamers: Incident Response and CTF Applications
When the next Log4Shell lands, your incident response question is “where does this component exist in our estate, and at what versions?” With a central SBOM registry, that’s a grep or a Dependency-Track query instead of a fire drill:
# Every asset shipping log4j-core?
jq -r '.components[] | select(.name=="log4j-core") | .version' sbom-*.cdx.json | sort -u
For practitioners building skills, run supply-chain exercises in your lab or CTF practice: generate SBOMs for a deliberately vulnerable image, plant an outdated package, and time how fast you can trace it via its purl and dependency path. Tools like OWASP Dependency-Track make a great self-hosted target—it ingests CycloneDX continuously and alerts on new CVEs against stored BOMs. Blue teamers who internalize this workflow turn supply-chain disclosure chaos into a lookup.
Operational Pitfalls: Stale SBOMs, Coverage Gaps, and Tooling Limits
Honest assessment time—SBOMs have real limitations:
- Generator-dependent accuracy. Two tools scanning the same image produce different BOMs. Binary-only components, vendored code, and stripped dependencies are frequent blind spots. Cross-check with a second generator for critical assets.
- Transitive dependency gaps. Lockfile-based generation misses system libraries; image scanning misses source-level dev dependencies. Choose scope deliberately.
- Staleness. An SBOM is a snapshot. If it’s not regenerated in CI on every build, it’s already wrong.
- No exploitation context. An SBOM tells you a vulnerable component exists—not whether it’s reachable. Pair it with VEX, reachability analysis, and KEV-list prioritization before triaging.
Adoption Checklist: Making SBOMs Table Stakes in Your Org
- Require SBOMs (CycloneDX or SPDX, NTIA minimum elements) from every software vendor in procurement contracts—this is already law for U.S. federal suppliers under M-22-18.
- Automate generation with Syft (or Trivy, or CDXgen) in CI on every build—no manual exports.
- Store all SBOMs in a central registry (Dependency-Track, Guac, or an object store + index) with unique build linkage.
- Wire Grype or equivalent scanning against stored SBOMs, with severity-based CI gates and allowlists.
- Adopt VEX to suppress non-exploitable findings and document risk acceptances.
- Rehearse: run a tabletop where a major CVE drops and time your “are we exposed?” answer. If it’s not under 30 minutes, fix the workflow—not the people.
SBOMs won’t stop supply-chain attacks. But like Zero Trust, they change your position from blind to informed—and when the next CVE-2021-44228-grade event hits, informed is the side you want to be on.
Frequently Asked Questions
What is an SBOM in simple terms?
A machine-readable ingredient list of all software components, versions, licenses, and dependencies in a product—like a nutrition label for software.
Should I use SPDX or CycloneDX?
CycloneDX is generally preferred for security use cases; SPDX for licensing and compliance. Many organizations produce both from the same scanning pipeline.
Is an SBOM the same as a dependency manifest?
No—lockfiles like package-lock.json cover one language ecosystem. SBOMs span compiled artifacts, OS packages, and system libraries across your whole build.
How do Syft and Grype work together?
Syft generates the SBOM inventory; Grype matches that inventory against vulnerability databases to surface known CVEs. One produces the “what’s in it,” the other answers “what’s broken in it.”
What is a VEX statement?
A machine-readable statement declaring whether a listed vulnerability actually affects your product (affected, not_affected, fixed, under_investigation)—cutting alert noise dramatically.
Related reading
- Post-Quantum Cryptography Explained: ML-KEM, ML-DSA and How NIST's Standards Change Your PKI
- Break Your Own PKI: A Hands-On Lab for Hash Collisions, Weak CSRs and Revocation Failures




