SPDX started as a license compliance format in 2010. Twelve years later, it's an ISO international standard (ISO/IEC 5962:2021) and one of the two dominant SBOM formats. But its license compliance roots show in the specification, and security teams often find SPDX verbose and confusing compared to CycloneDX.
That's partly fair and partly a familiarity problem. SPDX is powerful for security use cases once you know which parts to use and which to ignore for your purposes. This guide focuses on the security-relevant pieces.
SPDX Document Structure
An SPDX document has several sections:
- Document Creation Information -- who created the SBOM, when, with what tools
- Package Information -- the components (packages) in the software
- File Information -- individual files within packages (optional, often omitted for SBOMs)
- Snippet Information -- code snippets with different licensing (rarely used in SBOMs)
- Relationships -- how packages relate to each other
- Annotations -- notes and reviews
- Other Licensing -- custom license definitions
For security-focused SBOMs, you primarily care about sections 1, 2, 5, and the security-related extensions.
SPDX Formats
SPDX supports multiple serialization formats:
- JSON -- most practical for tooling integration
- Tag-Value -- human-readable, good for diffs
- XML -- structured, verbose
- YAML -- readable, less common
- RDF/XML -- semantic web format, rarely used
JSON is the right choice for most security workflows:
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "my-application",
"documentNamespace": "https://example.com/my-app-v1.2.3",
"creationInfo": {
"created": "2022-09-15T10:00:00Z",
"creators": [
"Tool: syft-0.60.0",
"Organization: Example Corp"
],
"licenseListVersion": "3.18"
}
}
The tag-value format is useful for quick inspection:
SPDXVersion: SPDX-2.3
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: my-application
DocumentNamespace: https://example.com/my-app-v1.2.3
Creator: Tool: syft-0.60.0
Creator: Organization: Example Corp
Created: 2022-09-15T10:00:00Z
Packages (Components)
SPDX packages are the equivalent of CycloneDX components:
{
"SPDXID": "SPDXRef-Package-npm-express-4.18.2",
"name": "express",
"versionInfo": "4.18.2",
"downloadLocation": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"supplier": "Organization: OpenJS Foundation",
"originator": "Person: TJ Holowaychuk",
"packageVerificationCode": {
"packageVerificationCodeValue": "d6a770ba38583ed4bb4525bd96e50461655d2758"
},
"checksums": [
{
"algorithm": "SHA256",
"checksumValue": "abcdef1234567890..."
}
],
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:npm/express@4.18.2"
},
{
"referenceCategory": "SECURITY",
"referenceType": "cpe23Type",
"referenceLocator": "cpe:2.3:a:expressjs:express:4.18.2:*:*:*:*:*:*:*"
}
],
"licenseConcluded": "MIT",
"licenseDeclared": "MIT",
"copyrightText": "Copyright (c) 2009-2022 TJ Holowaychuk"
}
Security-Critical Fields
For vulnerability matching, these fields matter most:
externalRefswithpurl-- Package URLs are the most reliable way to match components to vulnerability databasesexternalRefswithcpe23Type-- CPEs match against NVD dataversionInfo-- exact version for vulnerability range matchingchecksums-- verify component integrity and match against hash-based vulnerability databases
The External References Problem
SPDX's external reference system is more flexible but less structured than CycloneDX's dedicated fields. Security information goes into externalRefs with category SECURITY:
{
"referenceCategory": "SECURITY",
"referenceType": "advisory",
"referenceLocator": "https://nvd.nist.gov/vuln/detail/CVE-2022-24999"
}
Valid security reference types include cpe22Type, cpe23Type, advisory, fix, url, and swid. The advisory type links directly to vulnerability advisories.
Relationships
SPDX relationships describe how packages connect:
{
"relationships": [
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relationshipType": "DESCRIBES",
"relatedSpdxElement": "SPDXRef-Package-my-app"
},
{
"spdxElementId": "SPDXRef-Package-my-app",
"relationshipType": "DEPENDS_ON",
"relatedSpdxElement": "SPDXRef-Package-npm-express-4.18.2"
},
{
"spdxElementId": "SPDXRef-Package-npm-express-4.18.2",
"relationshipType": "DEPENDS_ON",
"relatedSpdxElement": "SPDXRef-Package-npm-body-parser-1.20.1"
}
]
}
Key relationship types for security:
DEPENDS_ON-- runtime dependency (most common)DEV_DEPENDENCY_OF-- development-only dependencyOPTIONAL_DEPENDENCY_OF-- optional dependencyCONTAINS-- package contains another package (e.g., a container contains OS packages)BUILD_TOOL_OF-- tool used to build the packageGENERATED_FROM-- source the package was built from
The distinction between DEPENDS_ON and DEV_DEPENDENCY_OF is critical for vulnerability prioritization. A critical CVE in a dev dependency doesn't affect production.
SPDX 2.3 Security Additions
SPDX 2.3 added security-focused features that bring it closer to parity with CycloneDX for vulnerability workflows:
Security External References
{
"referenceCategory": "SECURITY",
"referenceType": "advisory",
"referenceLocator": "https://github.com/advisories/GHSA-xxxx-yyyy-zzzz"
}
Annotations for VEX-like Assertions
While SPDX 2.3 doesn't have native VEX support, annotations can carry vulnerability status information:
{
"annotations": [
{
"annotationDate": "2022-09-15T10:00:00Z",
"annotationType": "REVIEW",
"annotator": "Person: Security Team",
"comment": "CVE-2022-24999: NOT_AFFECTED - vulnerable code path not reachable in our configuration"
}
]
}
This is a workaround. SPDX 3.0 introduces proper VEX support.
SPDX 3.0: The Next Generation
SPDX 3.0 is a major overhaul that introduces a profile-based architecture:
- Core -- base document structure
- Software -- software packages and files
- Security -- vulnerabilities, VEX, and security advisories
- Licensing -- license compliance data
- Build -- build provenance
- AI/ML -- machine learning model metadata
- Dataset -- dataset documentation
The security profile adds first-class vulnerability and VEX support, eliminating the need for workarounds:
{
"type": "VexAffectedVulnAssessmentRelationship",
"vulnerability": "CVE-2022-24999",
"from": "SPDXRef-Package-qs",
"assessedElement": "SPDXRef-Package-my-app",
"actionStatement": "Upgrade qs to version 6.5.3 or later"
}
SPDX 3.0 is still gaining tooling support in 2022, but it's the future direction of the specification.
Generating SPDX SBOMs
With Syft
# SPDX 2.3 JSON
syft dir:./my-project -o spdx-json > sbom.spdx.json
# SPDX 2.3 Tag-Value
syft dir:./my-project -o spdx-tag-value > sbom.spdx
With Trivy
trivy fs --format spdx-json --output sbom.spdx.json ./my-project
With the SPDX Tools
The SPDX project provides official tools for validation and conversion:
# Validate an SPDX document
pip install spdx-tools
pyspdxtools_parser sbom.spdx.json
# Convert between formats
pyspdxtools_converter sbom.spdx.json sbom.spdx.xml
SPDX vs CycloneDX: When to Use Which
Use SPDX when:
- Regulatory requirements reference ISO/IEC 5962
- License compliance is a primary concern
- You need to document file-level information
- Your toolchain already produces SPDX (e.g., Yocto)
Use CycloneDX when:
- Security and vulnerability management are the primary use cases
- You need service definitions (SaaSBOM)
- You want a leaner, more focused specification
- Your toolchain has better CycloneDX support
In practice, support both. The industry hasn't converged on one format, and conversion tools bridge the gap.
How Safeguard.sh Helps
Safeguard ingests both SPDX and CycloneDX SBOMs natively. Upload SPDX 2.3 documents and the platform extracts packages, relationships, external references, and security metadata for continuous vulnerability monitoring. Safeguard resolves Package URLs and CPEs from SPDX external references to match against live vulnerability feeds. If your supply chain produces SPDX and your security team thinks in CycloneDX, Safeguard normalizes both into a unified view so the format doesn't dictate the workflow.