The Cyber Resilience Act (CRA) mandates that manufacturers of digital products provide a machine-readable software bill of materials (SBOM) for every release. Annex I.2(1)(d) leaves the format open, but the choice carries long-term compliance, interoperability, and cost consequences. This article compares SPDX, CycloneDX, and SWID against the CRA’s requirements, maps them to the NTIA minimum elements, and recommends tooling for each major ecosystem. Mise à jour du 31/07/2026 : SWID a été retiré
Mise à jour du 31 juillet 2026. Les *2026 Minimum Elements for a SBOM* v2.1, publiés le 29 juillet 2026 par la CISA avec l'ANSSI, le BSI et 14 autres agences nationales, retirent SWID de la liste des formats de données SBOM reconnus — le motif avancé étant l'absence d'outillage suffisant. La comparaison à trois formats ci-dessous n'est donc plus d'actualité : le choix se joue désormais entre SPDX et CycloneDX. Cette mise à jour ne disqualifie PAS les balises SWID comme *identifiants de composant* : l'élémentComponent Identifiersaccepte explicitement les identifiants propres à une organisation et les identifiants intrinsèques. Ce qui disparaît, c'est SWID comme format du document SBOM, pas comme moyen d'identifier un composant à l'intérieur. Autres changements du même texte :Supplier NamedevientComponent Producer, l'élémentAccess Controlest supprimé, et sept champs apparaissent — dontSBOM Author SignatureetSBOM Timestamp.
The Cyber Resilience Act (CRA) mandates that manufacturers of digital products provide a machine-readable software bill of materials (SBOM) for every release. Annex I.2(1)(d) leaves the format open, but the choice carries long-term compliance, interoperability, and cost consequences. This article compares SPDX, CycloneDX, and SWID against the CRA’s requirements, maps them to the NTIA minimum elements, and recommends tooling for each major ecosystem.
---
1. CRA Annex I.2(1)(d) SBOM mandate: what ‘machine-readable’ actually means
The CRA does not prescribe a specific format, but the term “machine-readable” in Annex I.2(1)(d) implies three technical constraints:
- Structured syntax: JSON, XML, YAML, or tag-value pairs that parsers can ingest without manual intervention.
- Standardised semantics: A published schema or specification that defines fields such as component name, version, supplier, and licence.
- Unique identifiers: CPE, PURL, or SWID tags that enable automated vulnerability matching against sources like the NVD or ENISA’s vulnerability database.
The European Commission’s delegated acts (expected Q4 2025) may further clarify whether the SBOM must be embedded in the product (e.g., as a SWID tag) or delivered as a separate file. Until then, manufacturers must choose a format that satisfies both the CRA and the NTIA’s *Minimum Elements for a Software Bill of Materials* (2021), which the Commission has informally endorsed.
---
2. Comparison matrix: SPDX 2.3 vs CycloneDX 1.6 vs SWID Tags
| Criterion | SPDX 2.3 (ISO/IEC 5962) | CycloneDX 1.6 | SWID Tags (ISO/IEC 19770-2) |
|---|---|---|---|
| Schema | JSON, YAML, RDF/XML, tag-value | JSON, XML | XML |
| Licence | CC-BY-3.0 | Apache-2.0 | ISO/IEC |
| Component identifiers | PURL, CPE, SPDXID | PURL, CPE, SWID | SWID (mandatory) |
| Dependency graph | Yes (relationships) | Yes (tree & graph) | No |
| Licence expression | SPDX licence list | SPDX licence list | No |
| Vulnerability extension | No (external VEX) | Built-in VEX | No |
| Tooling maturity | High (npm, Maven, Go) | High (npm, Maven, .NET) | Medium (Windows, firmware) |
| CRA Annex I.2(1)(d) fit | Full | Full | Partial (no dependency graph) |
| NTIA minimum elements | Full | Full | Partial |
SPDX and CycloneDX both satisfy the CRA’s “machine-readable” requirement and the NTIA’s minimum elements. SWID tags are mandated by the US *NTIA SBOM Minimum Elements* for firmware and Windows applications, but their lack of a dependency graph makes them insufficient for most CRA-covered products.
---
3. Sample SBOM excerpts
CycloneDX 1.6 JSON (minimal)
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"version": 1,
"components": [
{
"type": "library",
"name": "lodash",
"version": "4.17.21",
"purl": "pkg:npm/lodash@4.17.21",
"licenses": [
{
"license": {
"id": "MIT"
}
}
],
"supplier": {
"name": "OpenJS Foundation"
}
}
],
"dependencies": [
{
"ref": "pkg:npm/lodash@4.17.21",
"dependsOn": []
}
]
}SPDX 2.3 YAML (minimal)
SPDXID: "SPDXRef-DOCUMENT"
spdxVersion: "SPDX-2.3"
creationInfo:
created: "2024-10-15T00:00:00Z"
creators:
- "Tool: spdx-sbom-generator"
name: "example-app"
dataLicense: "CC0-1.0"
packages:
- SPDXID: "SPDXRef-Package-lodash"
name: "lodash"
versionInfo: "4.17.21"
downloadLocation: "NOASSERTION"
licenseConcluded: "MIT"
supplier: "Organization: OpenJS Foundation"
externalRefs:
- referenceCategory: "PACKAGE-MANAGER"
referenceType: "purl"
referenceLocator: "pkg:npm/lodash@4.17.21"Both excerpts include the NTIA minimum elements: supplier, component name, version, unique identifier (PURL), and licence. CycloneDX’s built-in dependencies array is more concise for dependency graphs; SPDX’s relationships section offers finer granularity but requires more tooling.
---
4. Generation tooling per ecosystem
| Ecosystem | Recommended tool (SPDX) | Recommended tool (CycloneDX) | Notes |
|---|---|---|---|
| npm | @cyclonedx/bom | @cyclonedx/bom | Single tool outputs both formats. |
| Maven | spdx-maven-plugin | cyclonedx-maven-plugin | CycloneDX plugin supports VEX. |
| pip | spdx-tools-python | cyclonedx-python | CycloneDX tool supports Poetry. |
| Cargo | cargo-spdx | cargo-cyclonedx | SPDX tool is less actively maintained. |
| Go | go-spdx | cyclonedx-gomod | CycloneDX tool supports Go modules. |
| .NET | Microsoft.SPDX | CycloneDX.NET | CycloneDX tool integrates with NuGet. |
| Docker | syft (SPDX & CycloneDX) | syft | Syft outputs both formats. |
Recommendation: Use @cyclonedx/bom for npm, cyclonedx-maven-plugin for Maven, and syft for container images. These tools are actively maintained, support both formats, and integrate with vulnerability disclosure workflows.
---
5. Vulnerability disclosure integration
The CRA requires manufacturers to disclose vulnerabilities “without undue delay” (Article 13(1)). SBOMs alone do not convey exploitability; Vulnerability Exploitability eXchange (VEX) documents are needed.
| Format | VEX integration method | CSAF support | OpenVEX support |
|---|---|---|---|
| SPDX 2.3 | External VEX (CSAF or OpenVEX) | Yes | Yes |
| CycloneDX 1.6 | Built-in vulnerabilities array | Yes | Yes |
| SWID | External VEX | No | No |
CycloneDX’s built-in VEX support simplifies compliance: a single JSON file can contain both the SBOM and the VEX statements. SPDX requires a separate VEX document, typically in CSAF or OpenVEX format.
Tooling:
vexctl(OpenVEX) for SPDX.cyclonedx-clifor CycloneDX VEX.csaf-toolsfor CSAF.
---
6. Storage and retention: 10-year obligation vs practical lifecycle
The CRA requires manufacturers to retain SBOMs for 10 years after the product ceases to be supported (Annex I.2(1)(d)). This exceeds typical cloud storage retention policies and necessitates a dedicated SBOM repository.
Storage options
| Option | Pros | Cons |
|---|---|---|
| Git repository | Versioned, auditable, free | Not scalable for large binaries |
| Artifact repository | Integrates with CI/CD (Nexus, Artifactory) | Licence costs for long-term retention |
| Object storage | Cheap, durable (S3, MinIO) | No built-in search or VEX integration |
| Dedicated SBOM platform | Search, VEX, compliance reporting | Vendor lock-in |
Retention workflow
- 1Generation: SBOM generated at build time (GitHub Actions, GitLab CI, Jenkins).
- 2Signing: SBOM signed with Sigstore or PGP (CRA Annex I.2(1)(e)).
- 3Storage: Pushed to object storage + SBOM platform.
- 4Retention policy: Object storage lifecycle rule to archive after 1 year, delete after 10 years.
- 5Access control: IAM policies restrict access to compliance and security teams.
Recommendation: Use a dedicated SBOM platform (e.g., Dependency-Track, FOSSA, or NexCyber) for search and VEX integration, and object storage for long-term retention.
---
Next step with NexCyber
The CRA’s SBOM mandate is not a one-time deliverable