Generate an SBOM for CRA: Tools, Formats, CI/CD
A hands-on guide to generating Software Bills of Materials for CRA compliance. Covers open-source tools, format selection, and automated pipeline integration.
In this article
The CRA requires a Software Bill of Materials. Every competitor article tells you this. None show you how to generate one.
This guide covers open-source tools, format selection, and CI/CD integration with no vendor lock-in required.
Tip: Start with Syft or Trivy for SBOM generation; both support CycloneDX and SPDX output and integrate easily into CI/CD pipelines.
Summary
- CRA requires machine-readable SBOMs covering "at least top-level dependencies"
- Recommended formats: CycloneDX 1.4+ or SPDX 2.3+ (per BSI TR-03183)
- Open-source tools: Syft (images/filesystems), Trivy (containers), cdxgen (source code)
- Integrate SBOM generation into CI/CD for automatic updates
- Quality matters: minimum fields include package name, version, supplier, hash, license
Important: The CRA requires machine-readable SBOMs covering all transitive dependencies. A simple package.json or requirements.txt is NOT sufficient.
What the CRA Actually Requires
Let's start with what the regulation says. Annex I, Part II of the CRA requires manufacturers to:
"identify and document vulnerabilities and components contained in products with digital elements, including by drawing up a software bill of materials in a commonly used and machine-readable format covering at the very least the top-level dependencies of the products"
Key points:
- Machine-readable format: Not a PDF, not a spreadsheet, but structured data
- At least top-level dependencies: The minimum scope, though more is better
- Not required to be public: Provided to authorities on request
- Must be updated: With each release, patch, or component change
The CRA doesn't mandate a specific format, but standardization efforts point clearly to CycloneDX and SPDX.
Format Selection: CycloneDX vs SPDX
Two formats dominate the SBOM landscape. Both are acceptable for CRA compliance.
CycloneDX
Origin: OWASP project, security-focused Current version: 1.6 (1.4+ recommended for CRA) Best for: Security and vulnerability management
Strengths:
- Native VEX (Vulnerability Exploitability eXchange) support
- Designed for security use cases
- Lighter specification, easier to implement
- Strong tooling ecosystem
- Direct CVE/vulnerability linking
JSON example:
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"version": 1,
"components": [
{
"type": "library",
"name": "lodash",
"version": "4.17.21",
"purl": "pkg:npm/lodash@4.17.21",
"hashes": [
{
"alg": "SHA-256",
"content": "cc6d..."
}
],
"licenses": [
{
"license": {
"id": "MIT"
}
}
]
}
]
}
SPDX
Origin: Linux Foundation, license compliance-focused Current version: 2.3 (ISO/IEC 5962:2021 standard) Best for: License compliance and legal review
Strengths:
- ISO international standard
- Comprehensive license expression syntax
- Strong in open source compliance contexts
- Longer track record
- Better for complex licensing scenarios
JSON example:
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "my-application",
"packages": [
{
"SPDXID": "SPDXRef-Package-lodash",
"name": "lodash",
"versionInfo": "4.17.21",
"downloadLocation": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"licenseConcluded": "MIT",
"checksums": [
{
"algorithm": "SHA256",
"checksumValue": "cc6d..."
}
]
}
]
}
Which to Choose?
| Use Case | Recommendation |
|---|---|
| Primary focus is security/vulnerabilities | CycloneDX |
| Primary focus is license compliance | SPDX |
| Need VEX integration | CycloneDX |
| Enterprise with existing SPDX tooling | SPDX |
| German market (BSI TR-03183) | Either (both recommended) |
| Starting fresh, no preference | CycloneDX (simpler, security-focused) |
For CRA compliance, either format works. Pick one and be consistent.
Open-Source SBOM Generation Tools
No vendor lock-in required. These tools are free, open-source, and production-ready.
Syft (Anchore)
Best for: Container images, filesystems, archives License: Apache 2.0 Output formats: CycloneDX, SPDX, Syft JSON
Installation:
# Linux/macOS
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
# Homebrew
brew install syft
# Docker
docker pull anchore/syft
Usage examples:
# Scan a container image
syft alpine:latest -o cyclonedx-json > sbom.cdx.json
# Scan a directory
syft dir:/path/to/project -o cyclonedx-json > sbom.cdx.json
# Scan an archive
syft /path/to/archive.tar.gz -o spdx-json > sbom.spdx.json
# Scan with specific catalogers (e.g., only Python)
syft dir:. -o cyclonedx-json --select-catalogers python
Supported ecosystems: Python, Node.js, Ruby, Java, Go, Rust, PHP, .NET, and more.
Trivy (Aqua Security)
Best for: Container images with built-in vulnerability context License: Apache 2.0 Output formats: CycloneDX, SPDX, plus vulnerability reports
Installation:
# Linux (Debian/Ubuntu)
sudo apt-get install trivy
# macOS
brew install trivy
# Docker
docker pull aquasec/trivy
Usage examples:
# Generate SBOM from container image
trivy image --format cyclonedx --output sbom.cdx.json alpine:latest
# Generate SBOM from filesystem
trivy fs --format cyclonedx --output sbom.cdx.json /path/to/project
# Generate SBOM with vulnerability info
trivy image --format cyclonedx --output sbom.cdx.json \
--scanners vuln nginx:latest
Advantage: Trivy can generate SBOMs and scan for vulnerabilities in one pass.
cdxgen (CycloneDX)
Best for: Source code analysis across many languages License: Apache 2.0 Output format: CycloneDX
Installation:
# npm (requires Node.js)
npm install -g @cyclonedx/cdxgen
# Docker
docker pull ghcr.io/cyclonedx/cdxgen
Usage examples:
# Scan current directory
cdxgen -o sbom.json
# Scan specific project type
cdxgen -t python -o sbom.json
# Scan with deep dependency resolution
cdxgen --deep -o sbom.json
# Scan a specific directory
cdxgen -o sbom.json /path/to/project
Supported languages: JavaScript, Python, Java, Go, Rust, PHP, Ruby, .NET, C/C++, and more.
Tool Comparison
| Feature | Syft | Trivy | cdxgen |
|---|---|---|---|
| Container images | Excellent | Excellent | Good |
| Source code | Good | Good | Excellent |
| Filesystem scan | Excellent | Good | Good |
| Vulnerability scanning | No (use Grype) | Yes | No |
| CycloneDX output | Yes | Yes | Yes |
| SPDX output | Yes | Yes | No |
| Speed | Fast | Medium | Medium |
| Language coverage | Very broad | Broad | Very broad |
Recommendation: Start with Syft for most use cases. Add Trivy if you need integrated vulnerability scanning. Use cdxgen for complex source code projects.
CI/CD Integration
Manual SBOM generation doesn't scale. Integrate it into your build pipeline.
GitHub Actions
name: SBOM Generation
on:
push:
branches: [main]
release:
types: [published]
jobs:
sbom:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Syft
run: |
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
- name: Generate SBOM
run: |
syft dir:. -o cyclonedx-json > sbom.cdx.json
- name: Upload SBOM as artifact
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.cdx.json
retention-days: 90
# Optional: Upload to CRA Evidence
- name: Upload to CRA Evidence
if: github.event_name == 'release'
env:
CRA_EVIDENCE_TOKEN: ${{ secrets.CRA_EVIDENCE_TOKEN }}
run: |
curl -X POST https://api.craevidence.com/api/v1/ci/sbom \
-H "Authorization: Bearer $CRA_EVIDENCE_TOKEN" \
-F "file=@sbom.cdx.json" \
-F "product_id=${{ vars.PRODUCT_ID }}" \
-F "version=${{ github.ref_name }}"
GitLab CI
generate-sbom:
stage: build
image: anchore/syft:latest
script:
- syft dir:. -o cyclonedx-json > sbom.cdx.json
artifacts:
paths:
- sbom.cdx.json
expire_in: 90 days
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_TAG
Jenkins
pipeline {
agent any
stages {
stage('Generate SBOM') {
steps {
sh '''
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b .
./syft dir:. -o cyclonedx-json > sbom.cdx.json
'''
}
}
stage('Archive SBOM') {
steps {
archiveArtifacts artifacts: 'sbom.cdx.json', fingerprint: true
}
}
}
}
Docker Build Integration
Generate SBOM during Docker build:
# Multi-stage build with SBOM generation
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Generate SBOM in build stage
FROM anchore/syft:latest AS sbom
COPY --from=builder /app /app
RUN syft dir:/app -o cyclonedx-json > /sbom.cdx.json
# Final image
FROM node:20-slim
COPY --from=builder /app/dist /app
COPY --from=sbom /sbom.cdx.json /app/sbom.cdx.json
CMD ["node", "/app/index.js"]
SBOM Quality: Meeting TR-03183
The German BSI Technical Guideline TR-03183 extends the NTIA minimum elements. While not legally required across the EU, following TR-03183 ensures high-quality SBOMs.
Required Fields
| Field | Required By | Notes |
|---|---|---|
| Component name | NTIA + TR-03183 | Package identifier |
| Version | NTIA + TR-03183 | Exact version string |
| Supplier | NTIA + TR-03183 | Vendor or maintainer |
| Unique identifier | NTIA + TR-03183 | PURL recommended |
| Dependency relationship | NTIA + TR-03183 | Direct vs transitive |
| Author of SBOM | NTIA + TR-03183 | Who created the SBOM |
| Timestamp | NTIA + TR-03183 | When SBOM was created |
| Hash values | TR-03183 | SHA-256 minimum |
| License | TR-03183 | SPDX license ID |
| Source repository | TR-03183 | VCS URL if available |
Validating SBOM Quality
Use these tools to check your SBOM:
# Validate CycloneDX format
npm install -g @cyclonedx/cyclonedx-cli
cyclonedx validate --input-file sbom.cdx.json
# Check for minimum fields with jq
jq '.components[] | select(.version == null or .purl == null)' sbom.cdx.json
# Count components with hashes
jq '[.components[] | select(.hashes != null)] | length' sbom.cdx.json
Improving SBOM Quality
If your SBOM is missing data:
- Use lock files:
package-lock.json,Pipfile.lock,go.sumcontain more metadata - Scan built artifacts: More complete than source-only scans
- Combine tools: Different tools find different components
- Add manual entries: For commercial or internal components
Keeping SBOMs Current
An SBOM is a snapshot. It must be updated to remain useful.
When to Regenerate
- Every release (major, minor, patch)
- After dependency updates
- After security patches
- When build configuration changes
Versioning Strategy
product-v1.0.0-sbom.cdx.json
product-v1.0.1-sbom.cdx.json
product-v1.1.0-sbom.cdx.json
Or use timestamps:
product-sbom-2026-01-15T10-30-00Z.cdx.json
Retention
The CRA requires 10-year retention. Store historical SBOMs:
- In your artifact repository
- In S3/cloud storage with lifecycle policies
- In CRA Evidence for integrated compliance tracking
Common Pitfalls
One-time SBOM generation
Problem: Creating an SBOM once and never updating it.
Solution: Automate SBOM generation in CI/CD. Every build should produce a fresh SBOM.
Missing transitive dependencies
Problem: SBOM only lists direct dependencies, missing nested packages.
Solution: Use lock files, scan built artifacts, enable deep scanning options.
Wrong format version
Problem: Using CycloneDX 1.3 when TR-03183 recommends 1.4+.
Solution: Check your tool's output version. Update tools regularly.
No hash values
Problem: Components without cryptographic hashes can't be verified.
Solution: Ensure your tool includes hashes. Scan built artifacts rather than just source.
Manual creation
Problem: Hand-crafting SBOMs is error-prone and unsustainable.
Solution: Always automate. Manual entries only for components tools can't detect.
Ignoring internal components
Problem: Only documenting open-source dependencies, not proprietary code.
Solution: Internal components need documentation too. Add them manually or configure tools appropriately.
SBOM Implementation Checklist
SBOM IMPLEMENTATION CHECKLIST
FORMAT SELECTION:
[ ] CycloneDX 1.4+ or SPDX 2.3+ chosen
[ ] JSON format (machine-readable)
[ ] Decision documented
TOOLING:
[ ] Primary tool selected (Syft/Trivy/cdxgen)
[ ] Tool installed and tested locally
[ ] Output validated against schema
CI/CD INTEGRATION:
[ ] SBOM generation added to build pipeline
[ ] Artifacts stored with appropriate retention
[ ] Generation triggered on releases
QUALITY ASSURANCE:
[ ] All components have: name, version, supplier
[ ] Hash values present (SHA-256+)
[ ] License information included
[ ] Transitive dependencies captured
[ ] PURL identifiers used
OPERATIONS:
[ ] SBOM versioned alongside product releases
[ ] Historical SBOMs archived (10-year retention)
[ ] Process documented for team
OPTIONAL - CRA EVIDENCE INTEGRATION:
[ ] API token configured
[ ] Upload automated on release
[ ] Quality scoring enabled
Related Articles
Does the CRA apply to your product?
Answer 6 simple questions to find out if your product falls under the EU Cyber Resilience Act scope. Get your result in under 2 minutes.
Ready to achieve CRA compliance?
Start managing your SBOMs and compliance documentation with CRA Evidence.