An SBOM (Software Bill of Materials) that lives in your docs folder and is manually updated before every release is not an SBOM — it's a liability. A real SBOM is generated automatically at build time, embedded in your artifact's metadata, and updated continuously.
This article covers the tactical implementation: integrating CycloneDX SBOM generation into GitHub Actions, GitLab CI, or your CI/CD platform, tagging AI-generated components, signing with Sigstore, and storing alongside your artifacts.
Why CycloneDX and Not SPDX?
Both are valid standards. Choose based on your use case:
| Standard | Best For | Notes |
|---|---|---|
| CycloneDX | Security-focused SBOMs | Vulnerability tracking, VEX, cryptographic signatures |
| SPDX | License compliance focus | Richer license metadata, open-source audits |
For regulatory compliance (EU CRA, FedRAMP, SOC 2): Use CycloneDX.
Why: CycloneDX is designed for security practitioners. It handles vulnerability metadata, VEX (Vulnerability Exploitability Exchange), AI-attribution tags, and cryptographic signatures natively. SPDX is stronger on licensing but weaker on security operations.
Most enterprises generate both: CycloneDX for internal security operations, SPDX for open-source license tracking.
CycloneDX Core Structures Reference
A CycloneDX SBOM is a JSON document with metadata, component list, and dependency graph:
{
"bomFormat": "CycloneDX",
"specVersion": "1.7",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"metadata": {
"timestamp": "2026-04-29T14:00:00Z",
"lifecycles": [{"phase": "build"}],
"tools": {
"components": [{
"type": "application",
"bom-ref": "tool-cdxgen-10.2.0",
"name": "cdxgen",
"version": "10.2.0"
}]
},
"component": {
"type": "application",
"name": "api-gateway",
"version": "2.14.1",
"supplier": {
"name": "Crash Override Labs",
"url": ["https://crashoverride.com"]
}
}
},
"components": [
{
"type": "library",
"bom-ref": "pkg:npm/[email protected]",
"name": "express",
"version": "4.18.2",
"purl": "pkg:npm/[email protected]",
"licenses": [{"license": {"name": "MIT"}}]
}
],
"dependencies": [
{
"ref": "pkg:npm/[email protected]",
"dependsOn": ["pkg:npm/[email protected]"]
}
]
}
Key fields:
| Field | Purpose |
|---|---|
bomFormat |
Always "CycloneDX" |
specVersion |
Version of spec (1.7 is current — released 2025-10-21, standardized as ECMA-424 on 2025-12-10; 1.6 still validates) |
serialNumber |
UUID for this SBOM (unique per build) |
version |
SBOM document version (increment if you patch the SBOM) |
metadata.timestamp |
When SBOM was generated |
metadata.component |
The artifact being documented |
components |
List of all dependencies |
dependencies |
Directed graph: who depends on whom |
Step 1: Generate SBOM from Build Artifact
You have three options:
Option 1: Source Analysis (Fast, Incomplete)
Use cyclonedx-py, cyclonedx-npm, or cyclonedx-gomod to analyze lockfiles:
# Node.js
npm install --save-dev @cyclonedx/cyclonedx-npm
npx cyclonedx-npm --output-file sbom.cdx.json
# Go
cyclonedx-gomod mod -output sbom.cdx.json
# Python
cyclonedx-py requirements requirements.txt -o sbom.cdx.json
Pros: Fast, works in CI easily
Cons: Misses vendored dependencies, embedded assets, build-time injections, transitive chains
Verdict: Use for quick testing, but not for production releases.
Option 2: Binary/Artifact Inspection (Accurate, Slower)
Use cdxgen or syft to analyze actual build artifacts (containers, binaries, bundles):
# For container images
cdxgen -t docker docker.io/myorg/api-gateway:v2.14.1 -o sbom.cdx.json
# For filesystem/artifacts
syft /path/to/artifact -o cyclonedx-json > sbom.cdx.json
# For multi-ecosystem projects
cdxgen --recursive /path/to/project -o sbom.cdx.json
Pros: Finds everything (vendored, embedded, transitive)
Cons: Slower, requires artifact availability
Verdict: Use for production SBOMs. This is accurate.
Option 3: Hybrid (Recommended)
Combine source analysis (fast, for early validation) with artifact inspection (accurate, for release):
# In CI: Quick sanity check
npx cyclonedx-npm --output-file sbom-lint.cdx.json
node tools/validate-sbom.js sbom-lint.cdx.json || exit 1
# Before release: Accurate SBOM from artifact
docker build -t api-gateway:v2.14.1 .
cdxgen -t docker docker.io/myorg/api-gateway:v2.14.1 -o sbom-release.cdx.json
Step 2: CI/CD Integration (GitHub Actions Example)
Here's a production-ready GitHub Actions workflow:
name: Build and Generate SBOM
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: myorg/api-gateway
jobs:
build-and-sbom:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Build and push container image
- name: Build and push
id: build
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Install SBOM tools
- name: Install cdxgen and Sigstore
run: |
npm install -g @cyclonedx/cdxgen
curl -sSLO https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
# Generate SBOM from container image
- name: Generate SBOM
run: |
cdxgen \
-t docker \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
-o sbom.cdx.json
# Add AI-attribution metadata (if applicable)
- name: Add AI metadata to SBOM
if: contains(github.event.head_commit.message, '[ai-generated]')
run: |
node tools/add-ai-metadata.js \
sbom.cdx.json \
--agent "copilot/coding-agent" \
--model "gpt-4-code" \
--commit "${{ github.sha }}"
# Validate SBOM
- name: Validate SBOM
run: |
npm install -g @cyclonedx/cli
cyclonedx-cli validate \
--input-file sbom.cdx.json \
--fail-on-errors
# Sign SBOM with Sigstore
- name: Sign SBOM attestation
if: github.event_name != 'pull_request'
run: |
cosign attest \
--predicate sbom.cdx.json \
--type cyclonedx \
--yes \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
# Store SBOM in artifact
- name: Upload SBOM artifact
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.cdx.json
# Publish SBOM to well-known location (for compliance scanning)
- name: Publish SBOM
if: github.event_name != 'pull_request'
run: |
# Copy SBOM to .well-known/sbom/ for public access
mkdir -p $GITHUB_WORKSPACE/.well-known/sbom
cp sbom.cdx.json $GITHUB_WORKSPACE/.well-known/sbom/api-gateway-${{ github.sha }}.cdx.json
cp sbom.cdx.json $GITHUB_WORKSPACE/.well-known/sbom/api-gateway-latest.cdx.json
Key points:
- Generate SBOM from artifact (
cdxgen -t docker) — Use the actual container image - Validate SBOM (
cyclonedx-cli validate) — Catch malformed SBOMs before release - Add AI metadata — Tag components generated by agents
- Sign with Cosign (
cosign attest) — Cryptographic proof of origin - Store with artifact — SBOM travels with the container image
Step 3: AI-Attribution Metadata
When an AI agent generates code or patches, add metadata to the SBOM:
{
"type": "library",
"name": "auth-service",
"version": "2.14.1",
"purl": "pkg:npm/[email protected]",
"x-ai-generated": {
"agent": "copilot/coding-agent",
"model": "gpt-4-turbo",
"model_version": "2026-q1-v4",
"prompt": "Generate login handler with OAuth2 support",
"generated_at": "2026-04-29T10:00:00Z",
"reviewed_by": "[email protected]",
"reviewed_at": "2026-04-29T11:30:00Z"
}
}
This lets auditors and compliance tools understand:
- Which code came from an agent
- Which agent and version
- Who reviewed it
- When it was reviewed
Step 4: Signing and Publishing
Sigstore (Recommended)
Keyless cryptographic signing using OIDC:
# Sign SBOM with Sigstore (keyless)
cosign attest \
--predicate sbom.cdx.json \
--type cyclonedx \
--yes \
docker.io/myorg/api-gateway:v2.14.1
# Verify signature — Sigstore public-good infra requires identity gating
# (tighten the regexp to your repo/org slug in production)
cosign verify-attestation \
--type cyclonedx \
--certificate-identity-regexp '.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
docker.io/myorg/api-gateway:v2.14.1
Benefit: No key management. GitHub Actions uses GitHub's OIDC token as proof of identity.
Traditional PKI
If you have your own signing keys:
# Sign with your RSA key (modern bundle pattern — single .sigstore.json
# file carries signature, certificate, and Rekor inclusion proof)
cosign sign-blob \
--key ~/.cosign/cosign.key \
--bundle sbom.cdx.json.sigstore.json \
sbom.cdx.json
# Verify with public key
cosign verify-blob \
--key cosign.pub \
--bundle sbom.cdx.json.sigstore.json \
sbom.cdx.json
Publishing SBOMs
Store alongside your artifacts:
https://yourcompany.com/.well-known/sbom/
├── api-gateway-v2.14.1.cdx.json
├── api-gateway-v2.14.1.cdx.json.sig
├── api-gateway-v2.14.0.cdx.json
└── api-gateway-v2.14.0.cdx.json.sig
This allows:
- Regulators to audit your SBOMs (EU CRA, FedRAMP)
- Customers to scan your dependencies (Snyk, Dependabot)
- Dependency tracking tools to correlate your code with vulnerabilities
Step 5: Vulnerability Scanning Integration
Once SBOM is generated, scan for known vulnerabilities:
# Using Grype
grype sbom:sbom.cdx.json
# Using Trivy
trivy sbom sbom.cdx.json
# Using Dependency-Track (enterprise)
curl -X POST https://dtrack.company.com/api/v1/bom \
-H "X-API-Key: $DTRACK_API_KEY" \
-F "projectName=api-gateway" \
-F "projectVersion=2.14.1" \
-F "[email protected]"
Output includes:
| Component | Version | CVE | Severity | Fixed In |
|---|---|---|---|---|
| lodash | 4.17.20 | CVE-2021-23337 | High | 4.17.21 |
| express | 4.18.0 | CVE-2024-9999 | Critical | 4.18.2 |
GitLab CI Example
If you use GitLab:
stages:
- build
- sbom
- sign
- publish
variables:
REGISTRY: registry.gitlab.com
IMAGE_NAME: myorg/api-gateway
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA .
- docker push $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA
generate-sbom:
stage: sbom
image: node:18
script:
- npm install -g @cyclonedx/cdxgen
- cdxgen -t docker $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA -o sbom.cdx.json
- cyclonedx-cli validate --input-file sbom.cdx.json --fail-on-errors
artifacts:
paths:
- sbom.cdx.json
sign-sbom:
stage: sign
image: gcr.io/projectsigstore/cosign:latest
script:
- cosign attest --yes --predicate sbom.cdx.json --type cyclonedx $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA
only:
- tags
publish-sbom:
stage: publish
image: alpine
script:
- mkdir -p /tmp/sbom
- cp sbom.cdx.json /tmp/sbom/api-gateway-$CI_COMMIT_TAG.cdx.json
- # Copy to web server or S3
artifacts:
paths:
- /tmp/sbom/
Common SBOM Commands Reference
# Validate SBOM schema
cyclonedx-cli validate --input-file sbom.cdx.json
# Convert JSON to XML
cyclonedx-cli convert --input-file sbom.cdx.json --output-file sbom.cdx.xml
# Merge two SBOMs
cyclonedx-cli merge --input-files sbom1.cdx.json sbom2.cdx.json --output-file merged.cdx.json
# Query components with jq
jq '.components[] | {name, version}' sbom.cdx.json
# Find all transitive dependencies of a package
jq '.dependencies[] | select(.ref == "pkg:npm/[email protected]") | .dependsOn[]' sbom.cdx.json
# Generate VEX (Vulnerability Exploitability Exchange) with vexctl (OpenVEX)
vexctl create --product pkg:oci/[email protected] --vuln CVE-2024-9999 --status not_affected --justification code_not_present --file vex.openvex.json
Checklist: Production SBOM Pipeline
- SBOM generated from artifact (not just lockfile)
- SBOM validation in CI/CD pipeline (fails on invalid SBOM)
- SBOM signed with Sigstore or PKI
- SBOM published to
.well-known/sbom/before release - AI-attribution metadata added for agent-generated components
- SCA/vulnerability scanning runs on SBOM
- SBOM retention policy (minimum 3 years for compliance)
- Metrics tracked: vulnerability count per component, remediation time
References
- CycloneDX Specification — Full spec and examples
- cdxgen Documentation — Multi-ecosystem SBOM generator
- Sigstore Documentation — Keyless signing and verification
- CISA 2025 SBOM Minimum Elements — What to include
- OWASP CycloneDX Tool Center — Integration tools