131% more reported CVEs per day in 2026 than last year.
← Back to Glossary

Package URL

A package URL (purl) is a standard string that identifies a software package by its ecosystem, namespace, name and version, such as pkg:npm/[email protected].

Last updated: November 11, 2026

A package URL (purl) is a standard identifier for a software package that names it the way its own ecosystem does: pkg:type/namespace/name@version?qualifiers#subpath. It became an Ecma standard, ECMA-427, in December 2025 (1st edition, Ecma TC54), and the specification is maintained at package-url/purl-spec.

How a purl is built

Every purl starts with the scheme pkg. The type is the ecosystem or package manager (npm, pypi, cargo, deb, maven and so on). The namespace is an optional grouping such as an npm scope, a Maven group or a Linux distribution. Then come the name and the version. Qualifiers add facts the ecosystem needs to pin the exact package, such as the architecture or the distribution release, and the subpath points inside a package.

pkg:npm/%40angular/[email protected]
pkg:pypi/[email protected]
pkg:cargo/[email protected]
pkg:deb/ubuntu/[email protected]+dfsg-1ubuntu2.1?arch=arm64&distro=noble

The last one says more than "cpio 2.15": it is Ubuntu's own build for the Noble release on arm64. That matters because distributions backport security fixes into their own versions, so the same upstream version can be vulnerable in one distribution and fixed in another.

Why vulnerability matching depends on it

Advisory databases organized by package know a component by its ecosystem and name. The OSV schema requires ecosystem and name for every affected package and calls its purl field "optional but recommended"; GitHub's advisories are also listed per ecosystem package. When an SBOM component carries a purl, a tool can match it against those advisories exactly. Without one it has to guess from a name or a CPE, or it matches nothing, and the vulnerability goes unreported.

In a Yocto build we compared, the build system's own SBOM listed hundreds of Rust crates only as .crate files with a download location and no pkg:cargo purl, so those crates could not be matched against Rust advisories (see Yocto builds). Newer Yocto releases add ecosystem purls for Cargo and Go sources.

Package URL vs CPE

Package URLCPE
Who defines the nameThe package's ecosystemNVD's CPE dictionary
IdentifiesA package in a registry or distributionA product, including hardware and operating systems
Formatpkg:type/namespace/name@versioncpe:2.3:part:vendor:product:version:...
StandardECMA-427NISTIR 7695 (CPE 2.3)
Used byOSV, GitHub advisories, SBOM toolsNVD, many scanners
Typical failureMissing for vendored or hand-listed codeAmbiguous vendor and product names; no CPE data for many new CVEs

The rule of thumb: give every package that has an ecosystem a purl, and use a CPE for what has none, such as commercial software, firmware and hardware. Mismatched identifiers are one of the main causes of wrong findings, covered in how to reduce vulnerability noise.

Package URLs in SBOMs

CycloneDX and SPDX both carry a purl per component. CISA's 2026 Minimum Elements say component identifiers should use common identifiers "such as Common Platform Enumeration (CPE) and Package-URL (PURL)", and Germany's BSI TR-03183-2 requires such identifiers for each component where they exist. A missing purl is a field quality problem rather than a gap in SBOM completeness: the component is listed, but no advisory database can look it up.

CRACI records a package URL for every package a build pulls in, including each Rust crate in a Yocto build, so its SBOMs can be matched against advisories. See build-time SBOMs.