Package URL (purl): the SBOM component identifier, explained

Last updated:

Key takeaways

  • The specification's own example shows these assembled: pkg:npm/%40angular/[email protected] — type npm, namespace @angular (percent-encoded as %40angular), name animation, version 12.3.1.
  • It is now both a community-governed open specification and a formal standard.
  • Both are component identifiers an SBOM can carry, but they solve different problems.
  • Two of the identifier's heaviest real-world users spell out the requirement in their own published specifications.

A purl (Package URL) is the standardized identifier pkg:type/namespace/name@version?qualifiers#subpath that names one exact software package the way its own ecosystem's package manager would — an npm package, a Maven artifact, a PyPI wheel — rather than a free-text name a scanner has to guess at. Ecma International formally standardized it as ECMA-427 in December 2025, and CycloneDX and the OSV vulnerability schema already require or recommend it as the preferred way to identify a component without ambiguity.

Facts and quotations below come directly from the purl-spec project's own GitHub repository and packageurl.org documentation, the Ecma International standard page, and the CycloneDX and OSV schema documentation themselves — read live for this guide. Confirm exact field names against your own tooling's current spec version before relying on this guide for a specific integration.

What is a purl, exactly?

Per the specification's own documentation at packageurl.org, a purl is "a standard URL-based syntax to identify software packages, independent from their ecosystem or distribution channel." The purl-spec project, maintained on GitHub under the package-url organization, frames the goal as giving teams "a simple, consistent, and flexible approach to identifying software packages with precision and clarity." The problem it solves is a mundane but real one: a string like "openssl 3.0.2" is ambiguous — which ecosystem, which distribution, which exact build — while a purl encodes that context directly into the identifier itself.

What do the seven components of a purl mean?

Component Required? Meaning
scheme Required Always the constant pkg
type Required The package ecosystem — for example maven, npm, pypi
namespace Optional A name prefix, such as an npm scope or a Maven group ID
name Required The package name itself
version Optional The package version
qualifiers Optional Type-specific metadata, such as a Maven classifier or an architecture
subpath Optional A path within the package

The specification's own example shows these assembled: pkg:npm/%40angular/[email protected] — type npm, namespace @angular (percent-encoded as %40angular), name animation, version 12.3.1. Only scheme, type and name are mandatory; everything else is filled in when it is known and omitted when it is not, which is exactly the flexibility a partial-confidence firmware component match needs.

Is purl an official standard?

It is now both a community-governed open specification and a formal standard. The GitHub repository remains the actively maintained reference implementation and the registry of recognized package types, but per packageurl.org's own announcement, "PURL was approved by Ecma International as ECMA-427 1st Edition in December 2025." Ecma's own standard page confirms the formal title, "Package-URL (PURL) specification," and describes its purpose as to "[define] the Package-URL (PURL) syntax for identifying software packages independently from their ecosystem or distribution channel." For a procurement document or a technical documentation file that needs to cite a recognized standards body rather than a GitHub URL, ECMA-427 is now that citation.

How is a purl different from a CPE?

Both are component identifiers an SBOM can carry, but they solve different problems. A CPE (Common Platform Enumeration) is NIST's structured identifier, used throughout the National Vulnerability Database, that names a product and a version (or version range) in a vendor-defined taxonomy — useful for matching against published CVEs, but sometimes ambiguous or requiring manual curation when a vendor's own naming is inconsistent. A purl instead gives the exact coordinate a package manager itself would use to fetch that package: ecosystem, namespace, name and version, in the same structure the package was actually published under — deterministic by construction, because it mirrors reality rather than a separately maintained taxonomy. Neither one makes the other redundant; see our SBOM Minimum Elements guide for how the current CISA-led minimum-elements data fields list a Component Identifier as required and name both CPE and PURL as acceptable examples, without mandating one over the other.

Which SBOM and vulnerability tools actually require it?

Two of the identifier's heaviest real-world users spell out the requirement in their own published specifications. CycloneDX's JSON Reference states, for the purl field on a component: "Asserts the identity of the component using package-url (purl). The purl, if specified, must be valid and conform to the specification defined at: https://github.com/package-url/purl-spec." The OSV (Open Source Vulnerability) schema, used across the broader open-source vulnerability-database ecosystem, defines the same field for an affected package: "The purl field is a string following the Package URL specification that identifies the package, without the @version component. This field is optional but recommended." In both cases, purl is the preferred cross-tool key precisely because a free-text package name cannot be resolved unambiguously across ecosystems, and a purl can.

A workable approach for firmware and SBOM teams

  1. Let your SBOM tooling generate purls automaticallywherever it has matched a component to a known package manager ecosystem — most CycloneDX and SPDX generators already do this for dependencies pulled through npm, PyPI, Maven, Cargo and similar registries.
  2. Expect gaps for firmware-specific componentsVendor SDKs, statically linked blobs and binary-analysis matches with no package-manager origin often have no clean purl type; our firmware SBOM guide covers what to record instead (hash, supplier name, confidence level) when a purl genuinely does not apply.
  3. Carry both a purl and a CPE where you can— vulnerability feeds and scanners vary in which one they match against, and the NTIA/CISA minimum-elements rules accept either as a valid Component Identifier.
  4. Pin the purl-spec or ECMA-427 version you're validating againstin your own tooling documentation, since the type registry is still actively maintained and new ecosystem types are added over time.

SoftWSP's firmware SBOM process matches binary-analysis component findings against known package identifiers, including purl, so the result lines up with what CycloneDX- and OSV-aware scanners expect downstream: see device firmware security. For the regulatory side of why an SBOM needs a component identifier at all, start at SBOM Minimum Elements or CycloneDX vs SPDX.

This guide is general technical information, not a substitute for the current purl-spec, ECMA-427 or your own SBOM tooling's documentation.

Sources

  1. package-url/purl-spec — the official Package URL specification (GitHub)
  2. packageurl.org: PURL introduction and documentation
  3. Ecma International: ECMA-427, Package-URL (PURL) specification, 1st edition (December 2025)
  4. OWASP CycloneDX: JSON Reference v1.6 — components[].purl field definition
  5. OSV Schema: affected[].package.purl field definition
  6. CISA, NSA, FBI and 15 international partners: 2026 Minimum Elements for a Software Bill of Materials (SBOM), 29 July 2026

Frequently asked questions

What is a Package URL (purl)?

A purl is, per the specification's own documentation at packageurl.org, "a standard URL-based syntax to identify software packages, independent from their ecosystem or distribution channel." The purl-spec project (maintained on GitHub by the package-url organization) describes its goal as providing "a simple, consistent, and flexible approach to identifying software packages with precision and clarity." Where a CVE or CPE identifies a product in the abstract, a purl identifies one specific package exactly the way its own ecosystem's package manager would: an npm package, a Maven artifact, a PyPI wheel, each addressed the same structured way.

What does a purl actually look like, component by component?

The general form is scheme:type/namespace/name@version?qualifiers#subpath, built from seven components per the official specification: scheme (always the constant "pkg"), type (required — the package ecosystem, for example maven, npm or pypi), namespace (optional, a name prefix such as an npm scope or Maven group ID), name (required, the package name itself), version (optional), qualifiers (optional, type-specific metadata such as a Maven classifier or a Docker image tag), and subpath (optional, a path within the package). An example the specification itself gives: pkg:npm/%40angular/[email protected] — type npm, namespace @angular (percent-encoded), name animation, version 12.3.1.

Is purl an official standard, or just a GitHub convention?

Both, now. It started and is still actively developed as an open specification on GitHub, but per packageurl.org's own announcement, "PURL was approved by Ecma International as ECMA-427 1st Edition in December 2025," formally titled the "Package-URL (PURL) specification," which Ecma states "defines the Package-URL (PURL) syntax for identifying software packages independently from their ecosystem or distribution channel." The GitHub repository remains the actively maintained reference implementation and type registry; ECMA-427 is the formal standards-body anchor a procurement or compliance document can cite.

How is a purl different from a CPE?

They solve related but distinct identification problems. A CPE (Common Platform Enumeration), NIST's structured identifier used throughout the National Vulnerability Database, names a product and version range in a vendor-defined taxonomy that can be ambiguous or require manual curation to match correctly. A purl instead gives the exact coordinate a package manager itself would use — ecosystem, namespace, name and version — which is deterministic and machine-resolvable because it mirrors how the package is actually published and fetched. Neither replaces the other: our [SBOM Minimum Elements guide](/guides/sbom-minimum-elements/) covers how the current CISA-led minimum-elements rules list a component identifier, naming CPE and PURL as the two common examples, as one of an SBOM's required data fields.

Which SBOM and vulnerability tools actually require it?

CycloneDX's own JSON specification states, for a component's purl field: "Asserts the identity of the component using package-url (purl). The purl, if specified, must be valid and conform to the specification defined at: https://github.com/package-url/purl-spec." The OSV (Open Source Vulnerability) schema used by Google's and the broader open-source vulnerability database ecosystem defines it the same way for affected packages: "The purl field is a string following the Package URL specification that identifies the package, without the @version component. This field is optional but recommended." Both treat purl as the preferred cross-tool identifier precisely because, unlike a free-text package name, it resolves to one specific package without ambiguity.

Do I need to generate purls myself, or do my SBOM tools already do it?

Most modern SBOM generators (for build-time tools that already know the exact package manager, namespace, name and version they pulled in) and most binary/firmware component-identification tools populate a purl automatically once they have matched a component to a known package. The practical risk is not generating one correctly for an open-source dependency pulled through a standard package manager — it is the long tail of vendor SDKs, statically linked blobs and firmware components with no package-manager origin at all, where no purl type cleanly applies and a CPE, hash, or a plainly stated "identifier unknown" is the honest answer instead.

In this cluster

Package URL (purl): the SBOM component identifier, explained