When comparing two SPDX SBOMs, ignore document level fields that change on every run, namely the Created timestamp and the document namespace. Compare package names, versions, external identifiers, licenses, checksums and relationships. Those are the fields that describe the software rather than the file.
Document fields that always differ
The SPDX specification defines the Created field as the time the document was originally created, a required UTC timestamp. It defines the document namespace as a unique absolute URI, and states that if a document is updated and becomes a new version, a new URI shall be used. So by design these two values differ between any two documents, even identical content. Leave them out of a comparison.
The document identifier, SPDXRef-DOCUMENT, is fixed and will match anyway.
Fields that describe the software
| Field | Why compare it | What a change suggests |
|---|---|---|
| Package name | Basic identity | Addition, removal or rename |
| Package version | Release level | Update or downgrade |
| External references such as package URLs | Strong identity for matching | Identity change, lookalike risk |
| License concluded and declared | Obligations | Relicensing or a detection change |
| Checksums | Content identity | Different bytes, see below |
| Supplier or originator | Provenance | Change of publisher |
| Relationships | Structure | Different parent, new dependency path |
Identifiers inside the file can change too
SPDX element identifiers such as package IDs are local to the document and are often generated sequentially. They can differ between runs while describing the same package. If your diff keys on them, you will see false changes. Match on the package URL when available, otherwise on name plus version.
Steps
- Parse both files rather than diffing text.
- Drop the Created time and document namespace.
- Build a map from package URL to package record for each file.
- Compare the maps: keys only in one side are additions or removals.
- For keys on both sides, compare version, license, checksum and supplier.
- Compare relationships last, since they change whenever structure changes.
Mixed formats
If one file is SPDX and the other CycloneDX, convert one first or compare through a common model. Differences in how formats express fields, such as scope or licenses, can produce false changes. Convert, then spot check a few entries by hand.
Interpreting license fields
Values such as NOASSERTION mean the generator could not determine a value. A move from a real license to NOASSERTION is a detection loss, not a relicensing. Treat it as a tooling difference and check the generator version.
Automate carefully
A script can do this in under a hundred lines. Test it on two identical runs and confirm it reports nothing. That test catches most of the noise sources above.
Safeguard generates SBOMs and scans dependencies, and its policies can act on license and component changes. A project overview is available on the free scan.
Quick check
Why does the SPDX namespace differ between two runs?
The specification requires a new unique URI for each new document version, so it always differs.
Should I match packages on SPDX element IDs?
No. They are local and can be regenerated. Match on package URL, or name plus version.
Sources
Last reviewed: 19 October 2026.