What Is an SBOM and Why It Matters
A Software Bill of Materials is an ingredient list for software. It declares every component — libraries, frameworks, modules, and their versions — that makes up your application. Think of it as a nutrition label for code.
Before Log4Shell (December 2021), most organizations could not answer a simple question: "Do we use Log4j, and where?" Teams spent days or weeks auditing every service, container, and deployment. Organizations with SBOMs answered in minutes.
An SBOM lets you:
- Respond to zero-day disclosures in hours instead of weeks
- Track license obligations across your software supply chain
- Meet regulatory requirements (US federal contracts, EU Cyber Resilience Act)
- Verify that what you built is what you deployed (no tampering)
- Automate vulnerability monitoring across all components
This guide covers the full lifecycle: generating SBOMs, signing them for authenticity, and monitoring them for new vulnerabilities.
---
SBOM Formats: CycloneDX vs. SPDX
Two formats dominate the SBOM landscape. Choosing between them depends on your primary use case.
CycloneDX (OWASP)
CycloneDX was created by OWASP specifically for security use cases. It excels at vulnerability correlation — linking components to known CVEs.
Key characteristics:
- JSON and XML output formats
- Native support for vulnerability data (VEX — Vulnerability Exploitability eXchange)
- Designed for automation and machine consumption
- Lighter specification, easier to parse
- Supports components, services, and dependencies graph
- Version 1.5 and above includes formulation data (how the software was built)
Example CycloneDX JSON (abbreviated):
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"version": 1,
"metadata": {
"timestamp": "2026-07-02T10:00:00Z",
"component": {
"type": "application",
"name": "my-web-app",
"version": "2.1.0"
}
},
"components": [
{
"type": "library",
"name": "express",
"version": "4.18.2",
"purl": "pkg:npm/express@4.18.2",
"licenses": [{"license": {"id": "MIT"}}]
},
{
"type": "library",
"name": "lodash",
"version": "4.17.21",
"purl": "pkg:npm/lodash@4.17.21",
"licenses": [{"license": {"id": "MIT"}}]
}
]
}
SPDX (Linux Foundation)
SPDX (Software Package Data Exchange) is an ISO/IEC 5962:2021 international standard. It was designed for license compliance and is the format required by many legal and procurement teams.
Key characteristics:
- Tag-value, JSON, XML, and RDF output formats
- ISO standard (strongest for compliance documentation)
- Rich license expression syntax (MIT AND Apache-2.0)
- Package verification codes for integrity checking
- Relationship types between packages (DEPENDS_ON, CONTAINS, BUILD_TOOL_OF)
- Stronger in describing file-level licensing
Example SPDX JSON (abbreviated):
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "my-web-app-sbom",
"documentNamespace": "https://example.com/my-web-app-2.1.0",
"packages": [
{
"SPDXID": "SPDXRef-Package-express",
"name": "express",
"versionInfo": "4.18.2",
"downloadLocation": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"licenseConcluded": "MIT",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:npm/express@4.18.2"
}
]
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relationshipType": "DESCRIBES",
"relatedSpdxElement": "SPDXRef-Package-express"
}
]
}
When to Use Which
Choose CycloneDX when your primary goal is vulnerability management, you need to integrate with security tools like Dependency-Track or Grype, you want machine-readable automation-friendly output, or you need VEX support.
Choose SPDX when compliance with ISO standards is required, legal teams need license audit documentation, you are working with government contracts that specify SPDX, or you need file-level license attribution.
In practice, generate both. Tools like Syft output either format with a flag change. Many organizations generate CycloneDX for security workflows and SPDX for compliance records.
---
Generation Tools
Syft (by Anchore)
Syft generates SBOMs from container images, filesystems, archives, and source directories. It is the most versatile SBOM generator available.
Install:
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
Generate from a container image:
syft my-app:latest -o cyclonedx-json > sbom-cyclonedx.json
syft my-app:latest -o spdx-json > sbom-spdx.json
Generate from source directory:
syft dir:./src -o cyclonedx-json > sbom-source.json
Generate from a filesystem (after build):
syft dir:./dist -o cyclonedx-json > sbom-artifact.json
Sample output snippet (CycloneDX):
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"components": [
{
"type": "library",
"name": "github.com/gin-gonic/gin",
"version": "v1.9.1",
"purl": "pkg:golang/github.com/gin-gonic/gin@v1.9.1"
}
]
}
Trivy — SBOM as Part of Scanning
Trivy generates SBOMs alongside vulnerability scanning, giving you both in one pass:
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
Generate SBOM:
trivy image --format cyclonedx --output sbom.json my-app:latest
trivy image --format spdx-json --output sbom-spdx.json my-app:latest
trivy fs --format cyclonedx --output sbom-fs.json .
Scan an existing SBOM for vulnerabilities:
trivy sbom sbom.json
This is powerful — you can generate the SBOM once and scan it repeatedly as new vulnerabilities are disclosed without rebuilding the image.
Microsoft SBOM Tool
For .NET and Windows workloads, Microsoft's SBOM tool integrates with MSBuild:
dotnet tool install --global Microsoft.Sbom.DotNetTool
Generate:
sbom-tool generate \
-b ./bin/Release \
-bc ./src \
-pn my-dotnet-app \
-pv 1.0.0 \
-ps "My Organization" \
-nsb https://example.com/sbom
This produces SPDX 2.2 format by default, suitable for compliance documentation.
cdxgen — CycloneDX Generator
cdxgen supports over 30 languages and package managers:
npm install -g @cyclonedx/cdxgen
Generate for various ecosystems:
cdxgen -o sbom.json
cdxgen -t java -o sbom.json
cdxgen -t python -o sbom.json
cdxgen -t go -o sbom.json
cdxgen -o sbom.json --deep
The --deep flag performs recursive analysis of sub-projects and transitive dependencies.
---
Signing SBOMs
An unsigned SBOM is a claim. A signed SBOM is evidence. Signing proves the SBOM was generated by your CI system and has not been tampered with.
cosign (Sigstore) — Keyless Signing
cosign enables keyless signing using OIDC identity from your CI provider. No key management required — your GitHub Actions identity becomes the signing key.
Install:
go install github.com/sigstore/cosign/v2/cmd/cosign@latest
Or download binary:
curl -sSfL https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 -o /usr/local/bin/cosign
chmod +x /usr/local/bin/cosign
Sign an SBOM:
cosign sign-blob --yes sbom.json --bundle sbom.json.bundle
Verify the signature:
cosign verify-blob sbom.json \
--bundle sbom.json.bundle \
--certificate-identity "https://github.com/my-org/my-repo/.github/workflows/build.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
Attestation with in-toto Format
in-toto attestations wrap your SBOM in a standardized envelope that includes metadata about who generated it and when:
cosign attest --predicate sbom.json \
--type cyclonedx \
registry.example.com/my-app:latest
cosign verify-attestation \
--type cyclonedx \
--certificate-identity-regexp ".github.com/my-org/." \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
registry.example.com/my-app:latest
This attaches the SBOM directly to the container image in the registry. Anyone pulling the image can also pull and verify its SBOM.
Verifying SBOM Authenticity Before Consumption
Before trusting an SBOM from a third party, verify its signature:
cosign verify-attestation \
--type cyclonedx \
--certificate-identity-regexp ".github.com/vendor-org/." \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
registry.vendor.com/their-app:latest | \
jq -r '.payload' | base64 -d > verified-sbom.json
trivy sbom verified-sbom.json
This creates a chain of trust: the vendor signed the SBOM with their CI identity, you verify it matches their known identity, then you scan it for vulnerabilities.
---
Monitoring with Dependency-Track
Generating SBOMs is half the story. You need continuous monitoring — when a new CVE is published, which of your projects are affected?
Dependency-Track (by OWASP) is the leading open source platform for SBOM-based vulnerability monitoring.
Deploy Dependency-Track
Using Docker Compose:
version: '3.8'
services:
dtrack-apiserver:
image: dependencytrack/apiserver:latest
ports:
- "8081:8080"
volumes:
- dtrack-data:/data
environment:
- ALPINE_DATABASE_MODE=external
- ALPINE_DATABASE_URL=jdbc:postgresql://postgres:5432/dtrack
- ALPINE_DATABASE_DRIVER=org.postgresql.Driver
- ALPINE_DATABASE_USERNAME=dtrack
- ALPINE_DATABASE_PASSWORD=dtrack
depends_on:
- postgres
restart: unless-stopped
dtrack-frontend:
image: dependencytrack/frontend:latest
ports:
- "8080:8080"
environment:
- API_BASE_URL=http://localhost:8081
depends_on:
- dtrack-apiserver
restart: unless-stopped
postgres:
image: postgres:16-alpine
environment:
- POSTGRES_DB=dtrack
- POSTGRES_USER=dtrack
- POSTGRES_PASSWORD=dtrack
volumes:
- postgres-data:/var/lib/postgresql/data
restart: unless-stopped
volumes:
dtrack-data:
postgres-data:
Start it:
docker compose up -d
Access the dashboard at http://localhost:8080 (default credentials: admin/admin).
Upload SBOMs from CI
Use the Dependency-Track API to upload SBOMs automatically after each build:
curl -X POST "https://dtrack.example.com/api/v1/bom" \
-H "X-Api-Key: $DTRACK_API_KEY" \
-H "Content-Type: multipart/form-data" \
-F "projectName=my-app" \
-F "projectVersion=2.1.0" \
-F "autoCreate=true" \
-F "bom=@sbom.json"
GitHub Actions step:
- name: Upload SBOM to Dependency-Track
run: |
curl -X POST "${{ secrets.DTRACK_URL }}/api/v1/bom" \
-H "X-Api-Key: ${{ secrets.DTRACK_API_KEY }}" \
-H "Content-Type: multipart/form-data" \
-F "projectName=${{ github.event.repository.name }}" \
-F "projectVersion=${{ github.sha }}" \
-F "autoCreate=true" \
-F "bom=@sbom.json"
Dashboard Capabilities
Once SBOMs are flowing in, Dependency-Track provides:
- Portfolio view showing all projects with vulnerability counts
- Component inventory listing every library version across all projects
- Vulnerability timeline tracking new CVEs affecting your components over time
- License risk identifying components with copyleft or unknown licenses
- Outdated component detection showing packages behind current versions
Policy Violations and Alerting
Configure policies to enforce organizational rules:
- No Critical Vulnerabilities: Condition is vulnerability severity equals CRITICAL, violation state is FAIL
- No GPL in Production: Condition is license family equals GPL, violation state is WARN
- Component Age: Condition is component outdated more than 365 days, violation state is INFO
Set up notifications to Slack, Microsoft Teams, email, or webhooks for real-time alerting when new vulnerabilities affect your portfolio.
---
Compliance Context
US Executive Order 14028
Issued May 2021, EO 14028 ("Improving the Nation's Cybersecurity") requires SBOM for software sold to the US federal government. Key requirements:
- Vendors must provide SBOMs for software delivered to federal agencies
- SBOMs must be machine-readable
- Must include direct and transitive dependencies
- Must be updated with each new release
- Format: SPDX or CycloneDX accepted
If you sell software to the US government or work with contractors who do, SBOM generation is not optional.
EU Cyber Resilience Act (CRA)
The EU CRA (expected enforcement 2027) requires:
- Products with digital elements must have documented SBOMs
- Vulnerability handling process must be in place
- Known vulnerabilities must be disclosed
- Security updates must be provided for the product's lifetime
- Applies to both commercial and open source software (with exceptions for non-commercial open source)
NTIA Minimum Elements for SBOM
The National Telecommunications and Information Administration defined minimum SBOM elements:
Required fields per component:
- Supplier name
- Component name
- Component version
- Unique identifier (purl recommended)
- Dependency relationship
- Author of SBOM data
- Timestamp of SBOM creation
Required practices:
- Automation support (machine-readable format)
- Known unknowns (declare what you could not identify)
- Frequency (new SBOM per release at minimum)
- Access control (determine distribution method)
---
Complete CI/CD Pipeline: Generate, Sign, Attach, Monitor
Here is a production pipeline implementing the full SBOM lifecycle:
name: SBOM Pipeline
on:
push:
branches: [main]
tags: ['v*']
permissions:
contents: read
packages: write
id-token: write
jobs:
build-and-sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Application
run: |
npm ci
npm run build
- name: Build Container Image
run: |
docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
- name: Install Syft
run: |
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
- name: Generate SBOM (CycloneDX)
run: |
syft ghcr.io/${{ github.repository }}:${{ github.sha }} \
-o cyclonedx-json > sbom-cyclonedx.json
- name: Generate SBOM (SPDX)
run: |
syft ghcr.io/${{ github.repository }}:${{ github.sha }} \
-o spdx-json > sbom-spdx.json
- name: Install cosign
uses: sigstore/cosign-installer@v3
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push Container Image
run: |
docker push ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Sign Container Image
run: |
cosign sign --yes ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Attach SBOM to Image
run: |
cosign attest --yes \
--predicate sbom-cyclonedx.json \
--type cyclonedx \
ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Upload SBOM to Dependency-Track
run: |
curl -X POST "${{ secrets.DTRACK_URL }}/api/v1/bom" \
-H "X-Api-Key: ${{ secrets.DTRACK_API_KEY }}" \
-H "Content-Type: multipart/form-data" \
-F "projectName=${{ github.event.repository.name }}" \
-F "projectVersion=${{ github.sha }}" \
-F "autoCreate=true" \
-F "bom=@sbom-cyclonedx.json"
- name: Upload SBOMs as Artifacts
uses: actions/upload-artifact@v4
with:
name: sbom-documents
path: |
sbom-cyclonedx.json
sbom-spdx.json
Verification by Consumers
Anyone consuming your container image can verify it:
cosign verify \
--certificate-identity-regexp ".github.com/your-org/." \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/your-org/your-app:latest
cosign verify-attestation \
--type cyclonedx \
--certificate-identity-regexp ".github.com/your-org/." \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/your-org/your-app:latest | jq -r '.payload' | base64 -d > verified-sbom.json
trivy sbom verified-sbom.json
---
Ongoing Monitoring Best Practices
SBOMs are not point-in-time documents. New vulnerabilities are disclosed daily.
SBOM Freshness Check
A stale SBOM gives false confidence. Enforce freshness:
sbom_date=$(jq -r '.metadata.timestamp' sbom.json)
age_seconds=$(( $(date +%s) - $(date -d "$sbom_date" +%s) ))
age_days=$(( age_seconds / 86400 ))
if [ "$age_days" -gt 7 ]; then
echo "SBOM is $age_days days old. Regenerate required."
exit 1
fi
Diffing SBOMs Between Releases
Track what changed between versions:
diff <(jq -r '.components[].purl' sbom-v1.json | sort) \
<(jq -r '.components[].purl' sbom-v2.json | sort)
This shows added, removed, and upgraded dependencies — useful for change review and audit trails.
Monitoring Lifecycle
---
Frequently Asked Questions
What is an SBOM and why is it important?
An SBOM (Software Bill of Materials) is a formal inventory of all components, libraries, and dependencies in your software, including their versions and licenses. It's important for security vulnerability tracking, license compliance, and supply chain transparency. When a new CVE is published, an SBOM lets you instantly know if you're affected without scanning every artifact.
How do I generate an SBOM?
Use tools like Syft (for container images and filesystems), CycloneDX plugins (for build tools like Maven, npm, pip), or SPDX tools for standard compliance. Integrate SBOM generation into your CI/CD pipeline after the build step. Generate in both CycloneDX (JSON) and SPDX formats for maximum compatibility with downstream tools and compliance requirements.
What is the difference between CycloneDX and SPDX formats?
CycloneDX is designed for application security with focus on vulnerability identification, dependency graphs, and DevSecOps integration. SPDX (Software Package Data Exchange) originated from license compliance with richer licensing metadata and is an ISO standard. CycloneDX is more common in DevSecOps pipelines while SPDX is preferred for legal/compliance use cases. Many tools support both.
How do I monitor SBOMs for new vulnerabilities?
Ingest SBOMs into a vulnerability management platform like Dependency-Track (open-source) that continuously correlates your component inventory against vulnerability databases (NVD, OSV). Set up alerts for new critical CVEs affecting your components. Automate ticket creation when vulnerabilities are discovered, and track remediation timelines by severity.
What is software supply chain security?
Software supply chain security protects the integrity of your software from source code through build to deployment. It includes verifying dependency sources, signing build artifacts, using SBOMs to track components, scanning for vulnerabilities, and ensuring build reproducibility. Frameworks like SLSA define maturity levels for supply chain security practices.
---
Related Resources
- DevOpsKit Prompt Library — 500 DevOps prompts including supply chain security automation
- Engineering Toolkit — Tools for SBOM generation and signing
- Production Troubleshooting — Scenarios including supply chain incident response
- SCA Dependency Scanning Guide — Vulnerability scanning that consumes SBOMs
- Docker Production Best Practices — Container security fundamentals