Tools

cdxgen v12: Reachability Evidence Lands in SBOMs

OWASP's cdxgen v12 ships reachability evidence powered by atom, multi-BOM generation (SBOM, CBOM, SaaSBOM, OBOM, CDXA), and CycloneDX 1.7 as the default. We tested it on a Java monorepo.

Yukti Singhal
Security Engineer
6 min read

OWASP cdxgen, the multi-language CycloneDX generator that started in 2021 as a single-purpose npm tool, has quietly turned into the most ambitious open-source BOM project in the ecosystem. The v12 line shipped during Q1 2026, with v12.1.4 the stable release at the time of writing and v12.3.0 expanding the toolkit beyond SBOM generation into reachability prioritization, SPDX export, and runtime risk analysis. We took v12.1.4 to a 460-module Java/Kotlin monorepo with a Python data-science sidecar and a Node.js front-end, and ran it head-to-head against Syft v1.20 and Trivy v0.70 across SBOM completeness, accuracy, and triage value.

What is new in cdxgen v12?

Four headline changes. First, the default CycloneDX spec is now 1.7 (cdxgen v11 still defaulted to 1.5). Second, cdxgen produces five distinct BOM types: classic Software BOM, Cryptography BOM (CBOM) for crypto-asset inventory, Operations BOM (OBOM), SaaS BOM (SaaSBOM), and a signed attestation document (CDXA) that wraps the BOM with provenance. Third, reachability analysis backed by the atom static-analysis engine adds callstack evidence to vulnerable components, so the report shows whether a vulnerable dependency is actually invoked from your source code. Fourth, v12.3.0 added an SPDX export path so teams that need to publish in SPDX 3.0 for regulatory reasons can do so without running a second tool.

The reachability output is the single most useful addition in years for false-positive triage. cdxgen used to give you the same answer Syft gives — here are the components, here are their PURLs, good luck. With v12, every vulnerable component carries an evidence.callstack block enumerating the source files and line numbers that reach into the library. On our Java monorepo, this immediately downgraded 117 of 312 critical findings from "must fix" to "vulnerable code path not present."

How does cdxgen v12 compare to syft and trivy for SBOM generation?

We benchmarked all three on the same monorepo, measuring component count, ecosystem coverage, evidence quality, and generation time.

| Tool | Components found | Ecosystems | Reachability evidence | Time (cold cache) | CycloneDX 1.7 | |---|---|---|---|---|---| | cdxgen v12.1.4 | 4,182 | 24 | Yes (atom) | 3m 14s | Default | | Syft v1.20 | 3,847 | 18 | No | 2m 47s | Opt-in | | Trivy v0.70 (sbom) | 3,612 | 16 | No | 1m 52s | Opt-in |

cdxgen's higher component count is partly real (it picks up Kotlin Gradle Catalog dependencies the others miss) and partly a categorization choice (it emits a separate component for each Gradle plugin and Kotlin compiler artifact). On evidence depth, cdxgen is now in its own class — Syft is still the best at fast container-image inventories but it is not chasing the same use case.

How does atom-powered reachability actually work?

Atom is a separate OWASP project — a multi-language code property graph for security analysis — and cdxgen embeds it for the SCA path. When you run cdxgen against a source tree with --evidence, it builds an atom graph, resolves PURLs against the OSV database, and for each PURL with a known vulnerable function, walks the call graph from your source files inward to determine whether the vulnerable function is reachable. The result is written into each component element as a pedigree block with analysis.justification set to either code_not_reachable or code_reachable. This matches the VEX justification vocabulary in CycloneDX 1.6 and 1.7, so downstream consumers — Dependency-Track, Safeguard, your auditor — can act on the evidence without bespoke parsing.

# Generate a full multi-BOM with reachability + signed attestation
cdxgen -t java -o bom.json --evidence \
  --required-only --include-formulation \
  --profile research \
  --author "platform-security@example.com" \
  ./services/

# Verify the signed attestation
cosign verify-blob \
  --certificate bom.json.crt \
  --signature bom.json.sig \
  --certificate-identity-regexp '^https://github\.com/example/.*' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  bom.json

What are the operational costs of switching to cdxgen v12?

Three to plan for. First, atom requires a working JVM 17+ on the build agent because the reachability engine runs as a Java process; lightweight Alpine-based runners that previously hosted Syft will need a heavier base image. Second, the cold-cache run is 70 seconds slower than Syft, and on monorepos the atom graph build can hit 10+ minutes on a 4-core agent — plan for a --reachables-slices-file cache that you persist across CI runs. Third, the multi-BOM output is opinionated: by default v12 emits SBOM and CBOM together, and if your downstream tooling chokes on the CBOM you must pass --only sbom to suppress it. The npm-based install also pulls in heavier transitive dependencies than v11, so air-gapped environments should mirror the cdxgen-plugins-bin release tarball rather than relying on the install-on-demand path.

Should you adopt cdxgen v12 as the primary BOM generator?

If you have a polyglot codebase and your downstream consumer (Dependency-Track, Safeguard, the customer audit team) cares about reachability or signed BOMs — yes. If you only need a fast container-image SBOM for vulnerability lookups, Syft still wins on speed. The pragmatic 2026 pattern most enterprise teams settle on is: Syft for container-image SBOMs in the image-build CI lane, cdxgen for application-source SBOMs in the service-build CI lane, both feeding into a single platform of record. cdxgen v12 also pairs nicely with Sigstore — the CDXA attestation output is signable with Cosign out of the box, which closes the chain from source-to-build-to-signed-BOM cleanly.

What does the multi-BOM output mean for compliance programs?

The five-BOM model is responsive to the way regulatory and customer requirements have multiplied since 2023. A federal-government customer may require a Software BOM in CycloneDX 1.6 plus a signed attestation; a financial-services audit may require a Cryptography BOM listing every TLS library version and cipher in use; an EU customer under DORA may ask for a SaaS BOM describing third-party services. cdxgen v12 produces all of these in one invocation, which means your build pipeline runs one tool rather than juggling four. The practical downside is that the multi-BOM output is verbose — a service that produced a 4 MB CycloneDX SBOM in v11 will produce a 12-15 MB bundle in v12 with all five BOM types, plus the signed attestation. Plan your storage and transmission accordingly; for most downstream consumers the right pattern is to upload only the SBOM and CDXA to the BOM registry and persist the supplementary BOMs in object storage with the CDXA referencing them.

How Safeguard Helps

Safeguard parses cdxgen v12's reachability evidence and the CDXA attestation block during ingest, lifting code_not_reachable findings into auto-suppressed state with the cdxgen evidence preserved as the audit trail. Griffin AI cross-references reachability output with runtime telemetry (where available) to confirm that a component flagged as unreachable in source is also absent from production process memory. The platform also acts as the canonical store across BOM types: cdxgen's CBOM feeds into the cryptographic-inventory dashboard, the SaaSBOM feeds into TPRM, and the CDXA attestation is verified before any BOM is accepted into the source-of-truth ledger. Your build pipeline produces the evidence; Safeguard turns it into policy.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.