Count how many components in the SBOM carry a PURL, then check that each has a version, a believable type, and qualifiers where needed. A PURL, short for Package URL, is a standard identifier such as pkg:pypi/django@1.11.1, and it is the quickest quality signal in the file.
Why PURLs show up in SBOMs
Package names are messy. The same library can be named differently by its project, its registry, and a Linux distribution. Matching by name alone invites mistakes. The NTIA minimum elements report calls this out: it describes the lack of a single, widely used name space for software and says to use an existing identifier when possible.
The PURL project was built to be that identifier. Its README says PURL is used in both CycloneDX and SPDX SBOMs, in most vulnerability databases including OSV, and in many package repositories. It is also now an Ecma standard, ECMA-427.
The seven parts
The README defines a PURL as seven components:
scheme:type/namespace/name@version?qualifiers#subpath
| Part | Required | What it means |
|---|---|---|
| scheme | Yes | Always pkg |
| type | Yes | The package ecosystem, such as maven, npm, pypi, nuget, gem, deb, rpm |
| namespace | No | A prefix like a Maven group, a Docker image owner, or a GitHub user |
| name | Yes | The package name |
| version | No | The package version |
| qualifiers | No | Extra data such as OS, architecture, or repository |
| subpath | No | A path inside the package |
Components run from most significant on the left to least significant on the right.
Reading real examples
These come straight from the project's examples:
pkg:npm/%40angular/animation@12.3.1is an npm package named animation under the angular scope. The%40is an encoded at sign.pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1is a Maven artifact, where the namespace is the group ID.pkg:deb/debian/curl@7.50.3-1?arch=i386&distro=jessieis a Debian package, with qualifiers for architecture and distribution.pkg:docker/cassandra@sha256:...identifies a container image by digest.
Read left to right. The type tells you which registry rules apply. The version tells you exactly which release. Qualifiers matter most for operating system packages, where the same version number can differ by distribution.
What to check in a received SBOM
- Does every component have one? Count components without a PURL. A few are normal, such as internal or vendored code. Most missing is a warning.
- Does the version match? A PURL with no version cannot be matched to a specific vulnerability range.
- Is the type plausible? A Python library tagged as maven suggests a tool error.
- Do qualifiers appear where they should? OS packages without a distro qualifier are harder to map to distribution advisories.
In a CycloneDX file, the PURL sits in a purl field on each component. In SPDX, it appears as an external reference of type purl on the package.
What a PURL does not do
A PURL identifies a package. It does not say the package is safe, patched, or reachable. It also does not tell you who supplied the copy you received, since two builds can share one PURL. Treat it as a lookup key, which is how the NTIA report describes other unique identifiers: they support automated mapping across data sources.
Where Safeguard fits
Safeguard offers SBOM and dependency scanning. You can run a free scan at gold.safeguard.sh/scan and compare component identifiers with the SBOM a supplier gave you.
Quick check
Is a PURL the same as a CPE?
No. Both are identifiers, but they come from different systems. The NTIA report lists CPE, SWID tags, and PURL as examples of other unique identifiers. A component can carry more than one.
Can a PURL point to a download link?
Not by itself. It identifies a package. Qualifiers like a repository URL can hint at where it came from, but it is not a download address.
Sources
Last reviewed: 19 October 2026.