Open-source Kubernetes SBOM scanner. Star us on GitHub
Documentation menu

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:

bom.cdx.json
{
  "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

FieldRole
bomFormat / specVersionFormat detection and schema validation ("CycloneDX", "1.6")
metadata.componentThe subject of the SBOM — for StackRadar, the container image itself
components[]The inventory: every package found in the image (may be nested)
components[].purlPackage URL — the primary key for vulnerability matching
components[].cpeCPE identifier, kept as a secondary matching signal where present
components[].bom-refDocument-internal ID that the dependency graph points at
dependencies[]Edges (refdependsOn) 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, or excluded.

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):

bash
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.json
Uploads require a cluster-scoped API key — see API keys & cluster scoping.

What 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.