SBOM formats
SPDX 2.3 vs. CycloneDX 1.7, when to pick which, ingest and conversion recipes.
Last updated July 30, 2026View source (v0.21.1)
Bomly reads and writes the two open SBOM standards used in production today. It writes SPDX 2.3 and CycloneDX 1.7, and ingests SPDX 2.3 plus CycloneDX 1.4 through 1.7.
What's an SBOM?
A Software Bill of Materials is a structured list of every package in a piece of software, with enough metadata (versions, licenses, suppliers, hashes) for an outside tool to make decisions about it. It is the dependency graph as a portable file.
You produce an SBOM once and consume it many times: in PR checks, in release artifacts, in supplier audits, in attestation pipelines.
Format comparison
| SPDX 2.3 | CycloneDX 1.7 | |
|---|---|---|
| Steward | Linux Foundation | OWASP |
| Primary use case | Software supply chain and license compliance | Component analysis and vulnerability management |
| Bomly write target | spdx | cyclonedx |
| Encoding | JSON (also Tag-Value and YAML upstream) | JSON (also XML upstream) |
| Vulnerability data | Add-on (SPDX 3.0) | First-class (vulnerabilities array) |
| File hashes | Yes | Yes |
| Relationship edges | Rich DESCRIBES, DEPENDS_ON, etc. | dependencies graph |
| Adoption | NTIA reference, ISO/IEC 5962 | OWASP standard, broad scanner support |
In practice: pick SPDX when a regulator or customer asks for it; pick CycloneDX when a vulnerability scanner is on the other end. Producing both is cheap.
Writing an SBOM
Use --format <format> for the primary stdout output, or -o <format>[=<path>] when you want an SBOM alongside another output. The format alone writes to stdout; format=path writes to a file:
# One format to stdout
bomly scan --format spdx
# One format to a file
bomly scan -o spdx=sbom.spdx.json
# Two formats in one scan
bomly scan \
-o spdx=sbom.spdx.json \
-o cyclonedx=sbom.cdx.json
# One format to stdout, one to a file
bomly scan -o spdx -o cyclonedx=sbom.cdx.json
Constraints:
- At most one
-omay omit=<path>. Two stdout outputs would collide. -o spdx=(empty path) is an error.- When every
-onames a file and--formatis not set, a successful run writes the files and prints nothing — add--format textif you also want the terminal report. --format spdx,--format cyclonedx,-o spdx, and-o cyclonedxare supported byscanonly.- Paths are resolved relative to the current working directory.
Ingesting an SBOM
Skip detection entirely and load an existing SBOM as input:
bomly scan --sbom --path ./vendor.spdx.json
This is fast, offline, and useful for:
- Auditing a vendor SBOM against your policy.
- Re-running policy on an SBOM you produced in a previous CI step.
- Diffing SBOMs across releases.
Format is auto-detected by content (both SPDX and CycloneDX JSON are supported).
Diffing SBOMs
Compare two SBOM files without re-running detectors on either side:
bomly diff --sbom --base ./v1.0.spdx.json --head ./v1.1.spdx.json
Useful for release notes, supplier-update reviews, and CI checks on prebuilt SBOMs.
What Bomly puts in the SBOM
Both formats carry:
- Package name, version, PURL.
- Dependency relationships from the detector graph.
- File-level evidence when the detector provided it.
When --enrich is set, components are enriched from the matching-stage package
registry (keyed by PURL):
- Licenses learned during matching (preferred over detection-time licenses).
- Content digests as component hashes (CycloneDX
hashes, SPDXchecksums). - CPEs (CycloneDX
cpe, SPDXSECURITY/cpe23Typeexternal references). - Vulnerabilities — CycloneDX as a first-class
vulnerabilitiesarray (ratings, CWEs, advisories,affects); SPDX asSECURITY/advisoryexternal references. - End-of-life status (CycloneDX
bomly:eol*properties, SPDX package comment).
Reachability annotations and other Bomly-specific metadata are emitted in the JSON output (--json or --format json), not in the standard SBOM formats. See Output formats.
Preservation and conversion limits
Bomly preserves component identity (including PURL), dependency edges, roots, scope, package type, licenses, digests, CPEs, and the enrichment fields described above when the destination format has an equivalent representation. Encoding is deterministic when the scan timestamp and document identifiers are fixed.
Some information necessarily becomes less specific during conversion:
- CycloneDX vulnerability records preserve ratings, CWEs, affected component references, descriptions, and advisory URLs. SPDX 2.3 represents each vulnerability as a package security advisory reference, so ratings, affected ranges, fix versions, and descriptions are not carried through an SPDX 2.3 round trip.
- Development scope maps to CycloneDX
excluded; runtime scope maps torequired. SPDX stores Bomly's normalized scope in the package comment. - Bomly relationship confidence (
direct,transitive, orunknown), source provenance, reachability analysis, policy findings, and run diagnostics are report data rather than portable SBOM fields. Use JSON when those distinctions must survive export and import. - A CycloneDX document has one metadata component. When an input graph has multiple roots, every root remains in the dependency graph, but only the first deterministic root is selected as that metadata component.
Before treating a generated file as a release artifact, validate it with the standard validator required by the receiving system. Bomly's tests parse every emitted target back through the corresponding typed codec and exercise round-trip identity and edge preservation; receiving systems can impose additional profile rules beyond the base format.
Format conversion
To convert between formats, run a scan and emit both in one pass:
bomly scan --sbom --path ./in.spdx.json --format cyclonedx > out.cdx.json
Bomly does not advertise a one-shot convert command — the scan pipeline is the conversion path.
See also
- Scan targets — every input Bomly accepts
- Output formats — text, JSON, SARIF, SBOM details
- SBOM detector — ingest specifics