CycloneDX SBOM format
A practical reference for the CycloneDX 1.6 JSON SBOMs StackRadar generates and ingests: components, PURLs, the dependency graph, and the fields used for vulnerability matching.
Why CycloneDX
CycloneDX is an OWASP-maintained SBOM standard (also published as ECMA-424) designed for security use cases. StackRadar generates and ingests CycloneDX 1.6 JSON. Uploads are validated against the official 1.6 JSON schema before ingestion, so malformed documents are rejected with a clear error rather than producing silently incomplete inventories.
A minimal example
This is the shape of what the scanner uploads for a container image:
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"version": 1,
"metadata": {
"component": {
"type": "container",
"name": "registry.example.com/shop/checkout",
"version": "sha256:9f2c…"
}
},
"components": [
{
"type": "library",
"bom-ref": "pkg:npm/express@4.19.2",
"name": "express",
"version": "4.19.2",
"purl": "pkg:npm/express@4.19.2",
"licenses": [{ "license": { "id": "MIT" } }]
},
{
"type": "library",
"bom-ref": "pkg:deb/debian/openssl@3.0.11-1~deb12u2",
"name": "openssl",
"version": "3.0.11-1~deb12u2",
"purl": "pkg:deb/debian/openssl@3.0.11-1~deb12u2?arch=amd64"
}
],
"dependencies": [
{
"ref": "pkg:npm/express@4.19.2",
"dependsOn": []
}
]
}The fields that matter
| Field | Role |
|---|---|
bomFormat / specVersion | Format detection and schema validation ("CycloneDX", "1.6") |
metadata.component | The subject of the SBOM — for StackRadar, the container image itself |
components[] | The inventory: every package found in the image (may be nested) |
components[].purl | Package URL — the primary key for vulnerability matching |
components[].cpe | CPE identifier, kept as a secondary matching signal where present |
components[].bom-ref | Document-internal ID that the dependency graph points at |
dependencies[] | Edges (ref → dependsOn) used to tell direct dependencies from transitive ones |
What StackRadar extracts
On ingestion, components are collected from every location the spec allows them — components[], nested sub-components, metadata.component, and build tools declared in metadata.tools.components[] — and stored with:
- Identity: name, group, version, type, PURL, CPE.
- Provenance: publisher, author, description.
- Integrity: cryptographic hashes, external references.
- Compliance: license IDs and expressions (useful well beyond CVE tracking).
- Graph position: whether the component is a direct dependency or pulled in transitively, derived from
dependencies[].dependsOn. - Scope:
required,optional, orexcluded.
Deduplication
Every uploaded document is content-hashed (SHA-256). If a pod restarts with an unchanged image, the scanner detects the existing hash via GET /v1/sboms/check and skips the upload entirely — so churn-heavy clusters don't generate duplicate SBOMs or wasted egress.
Uploading SBOMs yourself
The upload endpoint is public and doesn't care who generated the document — anything producing valid CycloneDX 1.6 JSON works (Syft, Trivy, cdxgen, your CI pipeline):
curl -X POST "https://api.stackradar.io/v1/sboms/upload/cyclonedx?projectId=<project-id>" \
-H "X-API-Key: $STACKRADAR_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @bom.cdx.jsonWhat about SPDX?
SPDX documents are detected on upload but not currently ingested — CycloneDX 1.6 is the supported format. If you have an SPDX-producing toolchain, most SBOM generators (including Syft and Trivy) can emit CycloneDX from the same scan.