Safeguard
Supply Chain•

What is a package URL (purl) in an SBOM and why does it matter?

A package URL, or purl, is a standard label for a software package. See how to read one, why names alone cause mismatches, and how it helps SBOM matching.

Safeguard Security Team
7 min read

A package URL, called a purl, is a short, standard text label that names one software package exactly: what kind it is, who publishes it, its name and its version. SBOMs use purls so that tools can match a component to vulnerability data without guessing.

TL;DR

  • A purl looks like a web address that starts with pkg:.
  • It packs the package type, name and version into one line.
  • Two packages can share a name in different ecosystems. A purl keeps them apart.
  • CISA's 2026 guidance tells SBOM authors to use common identifiers such as purl and CPE, and to include all of them if there are several.

Definition. The Package-URL specification describes a purl as a standardized URL based syntax that identifies software packages independent of their ecosystem or distribution channel. It is published as the Ecma standard ECMA-427.

Why do SBOMs need a special label for packages?

Imagine two people named Sam Lee. One is a dentist in Ohio. The other is a pilot in Oregon. If a letter says only "Sam Lee," it could reach the wrong one. You need more details.

Software has the same problem. A package name alone is often not enough. Different package systems, such as npm for JavaScript or PyPI for Python, can each have a package with the same name. A flaw reported in one should not be pinned on the other.

The purl project describes this challenge as "ambiguity in package identification," and says naming conventions differ across ecosystems. A purl fixes this by always including the ecosystem, which it calls the type.

The specification describes the result as a universal identifier with a predictable structure, so two tools that follow it produce the same label for the same package.

What does a purl look like?

The specification gives this layout:

scheme:type/namespace/name@version?qualifiers#subpath

Here is what each part means.

PartRequired?What it means
schemeYesAlways the word pkg
typeYesThe package system, such as maven, npm, nuget, gem or pypi
namespaceNoA prefix such as a Maven group, an image owner or an organization
nameYesThe package name
versionNoThe version
qualifiersNoExtra details such as operating system or architecture
subpathNoA path inside the package

The specification also says the parts form a hierarchy, from the most significant on the left to the least significant on the right.

Here are three real style examples taken from the project's own list:

pkg:pypi/django@1.11.1
pkg:nuget/EnterpriseLibrary.Common@6.0.1304
pkg:deb/debian/curl@7.50.3-1?arch=i386&distro=jessie

Read the first one aloud: "a package from PyPI, named django, version 1.11.1." Read the third: "a Debian package from the debian group, named curl, version 7.50.3-1, built for the i386 architecture on the jessie release."

How do I read a purl, step by step?

  1. Check the start. It should say pkg:. If it does not, it is not a purl.
  2. Find the type. The word after the colon tells you the ecosystem.
  3. Read the namespace and name. These sit between the first slash and the at sign. With some types the namespace is blank.
  4. Find the version. It follows the at sign.
  5. Look for a question mark. What follows holds qualifiers, written as key and value pairs.
  6. Look for a hash mark. What follows is a path inside the package.

One small detail. Special characters get encoded. In one of the specification's examples, an npm package written as @angular/animation appears as pkg:npm/%40angular/animation@12.3.1, where %40 stands for the at sign. That is normal, so do not treat it as a typo.

Why does this matter for vulnerabilities?

Most vulnerability matching comes down to one question: is this exact package, at this exact version, in the affected list? The purl project says purl is used as a standard identifier in a CycloneDX or SPDX SBOM, in most vulnerability databases such as OSV, and in many package repositories. It was also added to the CVE Record Format v5.2.0.

So a purl in your SBOM works like a key that fits many locks. A tool can take the purl and look it up in a vulnerability database directly, without parsing a messy name.

CISA's 2026 minimum elements point the same way. The Component Identifiers element says the field should use common software identifiers, such as the Common Platform Enumeration (CPE) and Package-URL (PURL), and that SBOM authors should "include all of them" if there are multiple.

The reason to include more than one is simple. Different databases key on different identifiers. The more keys you provide, the more likely a match works.

Worked example: a missed match

The names and numbers below are invented to show the problem.

A team's SBOM lists a component as "widget-core 2.4.1" with no ecosystem. The security database has two entries called "widget-core": one in a JavaScript registry and one in a Python registry. Only the JavaScript one has a known flaw in version 2.4.1.

Without a purlWith a purl
Tool sees "widget-core 2.4.1"Tool sees pkg:npm/widget-core@2.4.1
Cannot tell which ecosystemKnows it is the npm package
Either reports a flaw that does not apply, or misses a real oneMatches the correct entry
A person must investigateResult is clear

If the team's project is Python, the purl would read pkg:pypi/widget-core@2.4.1. The tool then correctly reports no match. Without the purl, the team might have spent an afternoon on a false alarm. Or, in the other direction, the tool might have missed a real flaw.

How do I check that my SBOM has good identifiers?

Here is a quick check you can do in a few minutes.

  1. Open your SBOM file in any text editor.
  2. Pick five components at random.
  3. For each one, look for a field that starts with pkg:.
  4. Confirm the type matches the ecosystem you know the package comes from.
  5. Confirm the version is filled in.
  6. If many components have no identifier, ask your tool how to turn on identifiers. If a value is truly unknown, the 2026 guidance says the author should state that it is unknown rather than leave it blank.

For more on reading the file itself, see how to read an SBOM and the CycloneDX walkthrough.

Try it yourself

In the demo below, open a dependency inventory and look at how each package is identified by its name, version and ecosystem together. Notice that the same name could mean different things without the ecosystem.

Click through it at your own pace, or open it full screen.

How can Safeguard help?

Safeguard's SBOM and dependency scanning builds an inventory of your direct and transitive dependencies and exports it in SPDX or CycloneDX, the two formats that carry purls. Reachability analysis then helps you focus on the matches your code can reach.

You can try a free scan at gold.safeguard.sh/scan. The Safeguard CLI and IDE extensions run the same kind of scan where developers work, and the CLI can also write code when a fix needs one.

Quick check

Is a purl the same as a link to a website?

No. It looks like a web address, but it is a label for a package, not a place you visit. It starts with pkg: and names the package type, name and version.

Do I have to write purls by hand?

No. SBOM tools create them from your project files. Your job is to check that they are present and correct.

What if a purl is missing from my SBOM?

Tools can still try to match by name, but matches become less reliable. Ask your SBOM tool to add identifiers, or mark them as unknown on purpose.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.