spdx-format
Reference for the SPDX (Software Package Data Exchange) v2.3 + v3.0 SBOM specification - Linux Foundation-curated, license-focused format covering packages, files, snippets, relationships, license declarations, and (in 3.0) AI / dataset / build / security profiles; supports Tag-Value / JSON / YAML / RDF / Spreadsheet encodings; preferred by US Federal procurement (NIST guidance) and Linux distros. Use when the team's SBOM consumer requires SPDX format (federal procurement, Linux Foundation members, license-compliance focus).
Install with skills.sh (any agent)
npx skills add testland/qa --skill spdx-formatspdx-format
Overview
SPDX is the Linux Foundation's SBOM standard, originally focused on license compliance. Per spdx.org/specifications (opens in new window):
Two active major versions:
| Version | Status | Notable |
|---|---|---|
| SPDX 2.3 | Stable; broadly tooled | Tag-Value / JSON / YAML / RDF / Spreadsheet; ISO/IEC 5962:2021 |
| SPDX 3.0 | Recent (2024); growing tooling | Profile-based: core + software + AI + dataset + build + security; JSON-LD primary |
This is a reference skill - defines the schema + tooling landscape; doesn't run scans. Pair with syft-generation to generate SPDX-format SBOMs.
When to use
For security-focused use cases, cyclonedx-format has richer first-class vuln support.
How to use
Step 1 - SPDX 2.3 top-level structure (JSON)
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "my-app-1.0.0-sbom",
"documentNamespace": "https://example.com/spdx/my-app/1.0.0",
"creationInfo": {
"created": "2026-05-06T12:00:00Z",
"creators": ["Tool: syft-1.16.0", "Organization: Acme Corp"]
},
"packages": [
{
"SPDXID": "SPDXRef-Package-myapp",
"name": "my-app",
"versionInfo": "1.0.0",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "Apache-2.0",
"licenseDeclared": "Apache-2.0"
},
{
"SPDXID": "SPDXRef-Package-lodash",
"name": "lodash",
"versionInfo": "4.17.20",
"downloadLocation": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"filesAnalyzed": false,
"licenseConcluded": "MIT",
"licenseDeclared": "MIT"
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relationshipType": "DESCRIBES",
"relatedSpdxElement": "SPDXRef-Package-myapp"
},
{
"spdxElementId": "SPDXRef-Package-myapp",
"relationshipType": "DEPENDS_ON",
"relatedSpdxElement": "SPDXRef-Package-lodash"
}
]
}Step 2 - Required fields per SPDX 2.3
Per spdx-spec (opens in new window):
| Field | Required? | Use |
|---|---|---|
spdxVersion | yes | Must be "SPDX-2.3" |
dataLicense | yes | "CC0-1.0" (CC0 - the SBOM data itself) |
SPDXID | yes | "SPDXRef-DOCUMENT" |
name | yes | Human-readable doc name |
documentNamespace | yes | Unique URI per BOM revision |
creationInfo.created | yes | ISO 8601 timestamp |
creationInfo.creators | yes | Tool / org / person who created |
packages[] | required for non-empty BOM | Inventory |
relationships[] | required (at least DESCRIBES) | Dep graph |
Step 3 - License expressions
SPDX is the canonical source for license identifiers (cross-format standard - even CycloneDX uses SPDX license IDs).
"licenseConcluded": "Apache-2.0",
"licenseConcluded": "MIT OR Apache-2.0",
"licenseConcluded": "(MIT AND BSD-3-Clause) OR GPL-2.0-only WITH Classpath-exception-2.0"Full list: spdx.org/licenses (current count ~600+).
The LicenseRef- prefix declares custom licenses:
"hasExtractedLicensingInfos": [{
"licenseId": "LicenseRef-AcmeProprietary",
"extractedText": "Acme Proprietary License Text..."
}],
"licenseConcluded": "LicenseRef-AcmeProprietary"Step 4 - Relationships
The relationships[] block is the dep-graph (SPDX equivalent of CycloneDX's dependencies[]):
| RelationshipType | Use |
|---|---|
DESCRIBES / DESCRIBED_BY | Document to top-level package |
DEPENDS_ON / DEPENDENCY_OF | Compile-time / runtime dep |
BUILD_DEPENDENCY_OF | Build-only dep |
DEV_DEPENDENCY_OF | Test/dev-only dep |
RUNTIME_DEPENDENCY_OF | Runtime-only dep |
OPTIONAL_DEPENDENCY_OF | Optional dep |
CONTAINS / CONTAINED_BY | Container to contained file/package |
GENERATED_FROM | Source-of-build |
STATIC_LINK / DYNAMIC_LINK | Linkage type |
Step 5 - Tag-Value format (SPDX-native)
Some toolchains use the SPDX Tag-Value format (older but well-tooled):
SPDXVersion: SPDX-2.3
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: my-app-1.0.0-sbom
DocumentNamespace: https://example.com/spdx/my-app/1.0.0
Creator: Tool: syft-1.16.0
Creator: Organization: Acme Corp
Created: 2026-05-06T12:00:00Z
PackageName: my-app
SPDXID: SPDXRef-Package-myapp
PackageVersion: 1.0.0
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageLicenseConcluded: Apache-2.0
PackageLicenseDeclared: Apache-2.0
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package-myappJSON is preferred for new toolchains; Tag-Value persists for legacy integrations.
SPDX 3.0 profiles and the SPDX tooling landscape are cataloged in references/spdx3-profiles-and-tooling.md; for most teams, stay on SPDX 2.3 unless 3.0 features are required.
Step 6 - Validation
# Python spdx-tools
pip install spdx-tools
pyspdxtools --infile sbom.spdx.json --version SPDX-2.3
# Validates structural conformance + license-expression syntax
# Validation via spdx online tool
# Upload to https://tools.spdx.org/app/Step 7 - CI integration
jobs:
spdx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: anchore/sbom-action@v0
with:
format: spdx-json
output-file: sbom.spdx.json
- run: |
pip install spdx-tools
pyspdxtools --infile sbom.spdx.json --version SPDX-2.3
- uses: actions/upload-artifact@v4
with:
name: spdx-sbom
path: sbom.spdx.jsonWorked example
A team must deliver an SPDX 2.3 SBOM to a US Federal procurement consumer for an app that bundles one proprietary component:
Result: a validated SPDX 2.3 JSON SBOM whose custom license resolves cleanly and whose relationship graph satisfies the federal consumer's format requirement.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
Skip documentNamespace | Can't deduplicate across BOM revisions | Generate unique URI per BOM |
| Use SPDX 3.0 with consumer expecting 2.3 | Tooling incompat | Stick to SPDX 2.3 unless 3.0 required (see references) |
Manual license assignment without LicenseRef- for custom | License-expression validation fails | Use proper SPDX license ID or LicenseRef- (Step 3) |
Skip relationships[] (just packages) | No dep-graph; downstream tools degrade | Always include DESCRIBES + DEPENDS_ON (Step 4) |
| Mix Tag-Value + JSON in same workflow | Toolchain confusion | Pick one format per workflow (Step 5) |
Limitations
References
SPDX 3.0 profiles and tooling
View source (opens in new window)SPDX 3.0 profiles and tooling
Extended reference extracted from spdx-format. See spdx.dev/specifications (opens in new window) for the source spec.
SPDX 3.0 profiles
SPDX 3.0 (2024 release) restructures into composable profiles:
| Profile | Use |
|---|---|
core | Minimum BOM model |
software | Software-specific extensions (approx. SPDX 2.3 packages) |
licensing | License identification + expressions |
security | Vulnerability + VEX statements |
ai | AI/ML models, datasets, hyperparameters |
dataset | Dataset-specific metadata |
build | Build provenance (similar to in-toto attestations) |
JSON-LD is the primary encoding; tooling support is growing but less mature than 2.3 as of 2026. For most teams, stay on SPDX 2.3 unless 3.0 features are required - 2.3 has broader tooling.
Tooling
| Tool | Use |
|---|---|
syft | Generates SPDX 2.3 (JSON / Tag-Value); cross-source |
spdx-tools | Reference impl (Python); validation + conversion |
spdx-tools-java | Java reference impl |
ORT (OSS Review Toolkit) | License compliance scanning + SPDX reporting |
spdx-sbom-generator | Per-language native generation |
tern | Container image SPDX generation |
Trivy | Cross-purpose scanner with SPDX output |
Related skills
cyclonedx-format
Reference for the CycloneDX v1.6 software bill of materials (SBOM) specification - OWASP-curated, security-focused format covering software components, services, dependencies, vulnerabilities, formulation, machine learning models, and SaaS BOMs; supports XML / JSON / Protobuf encodings; per-language generators for npm, pip, Maven, Gradle, Go, etc.; integrates with CI via generate + sign + attest workflow. Use when the user asks to generate or write a software bill of materials / SBOM in CycloneDX form, or when the team adopts CycloneDX as its primary SBOM format (preferred for security-focused use cases vs SPDX's licensing focus).
grype-scanning
Scans for vulnerabilities using Anchore Grype: `grype sbom:./sbom.json` / `grype {image}` / `grype dir:./` across OS-package + language-package ecosystems (Alpine / Debian / Ubuntu / RHEL / Amazon Linux / Ruby / Java / JavaScript / Python / .NET / Go / PHP / Rust). `.grype.yaml` per-CVE and per-package ignore rules with mandatory `expires:` dates and reachability justification (the Grype-native suppression path, distinct from standalone VEX document authoring in vex-author); EPSS + KEV + risk-score prioritization; OpenVEX assertion filtering; `--fail-on high/critical` CI gate. Use when the team wants Grype-native vuln scanning, or pairs with Syft (syft-generation) for an SBOM-driven workflow.
sbom-diff
Compares two CycloneDX or SPDX SBOMs to surface net-new, removed, and version-changed components between image or build versions; uses cyclonedx-cli diff for structured output and syft-based generation for the input SBOMs; gates CI on net-new component introduction; enables supply-chain alerting when unexpected dependencies appear across releases. Use when the team needs to detect dependency drift between container image builds, release candidates, or dependency-update branches.
syft-generation
Generates Software Bill of Materials (SBOMs) using Anchore Syft - supports container images / directories / archives across OCI / Docker / Singularity formats; output formats CycloneDX-JSON / SPDX-JSON / Syft-JSON / table / GitHub-JSON; pairs with `grype-scanning` for SBOM-driven vuln scanning. Use when the team needs SBOM artifacts for compliance (US EO 14028, EU CRA, FDA medical-device guidance) or as input to vuln scanners.
trivy-image
Configures and runs Trivy for container image scanning: Aqua Security's all-in-one scanner combining vuln + secret + misconfiguration + license detection in one pass; `trivy image {image}` with --severity HIGH,CRITICAL filter; --format sarif/json (incl. scan-embedded CycloneDX; for standalone SBOM generation see syft-generation + cyclonedx-format); .trivyignore CVE suppression file; --ignore-unfixed for actionable filter; --scanners vuln/misconfig/license/secret toggle. Use when the team wants a single tool covering container image security across multiple dimensions, not for producing a standalone CycloneDX SBOM.
vex-author
Authors and validates OpenVEX documents - produces `not_affected`, `affected`, `fixed`, and `under_investigation` statements with justification codes using `vexctl create`; attaches VEX assertions to container images; outputs `.openvex.json` files consumed on a downstream VEX-filter / vulnerability-prioritization path. Use when a scanner flags a CVE that analysis confirms is not exploitable in your deployment, and a machine-readable `not_affected` assertion is needed to suppress false positives without discarding the finding from the audit trail.