
Every component in your SBOM needs a unique identifier. CPE and PURL are the two that matter. They were built for different purposes, they don't map cleanly to each other, and getting that mapping wrong is a quiet but real way to ship a medical device with blind spots in your postmarket CVE monitoring.
Last reviewed: May 2026 against the FDA February 2026 final guidance, CycloneDX 1.6, SPDX 2.3, and NVD CPE Dictionary practice as of Q2 2026.
TL;DR - CPE vs PURL in one paragraph
PURLs identify open-source packages by where you got them - pkg:npm/lodash@4.17.21, pkg:pypi/cryptography@42.0.5, pkg:maven/org.openssl/[email protected]. They're generated automatically by SBOM tooling from package-manager metadata. CPEs identify products by NIST-curated strings - cpe:2.3:a:openssl:openssl:3.0.13:*:*:*:*:*:*:*. They're what the NVD attaches CVEs to. The FDA's 2026 guidance and CycloneDX/SPDX both accept PURL and CPE as valid component identifiers. In practice, your build-time SBOM is PURL-heavy and your postmarket vulnerability monitoring is CPE-heavy - and the mapping between them is where most medical device cybersecurity programs leak.
What CPE is
Common Platform Enumeration is a structured naming scheme NIST maintains as the official CPE Dictionary. Every entry is a 13-field colon-delimited string:
cpe:2.3:a:openssl:openssl:3.0.13:*:*:*:*:*:*:*
│ │ │ │ │ │
│ │ │ │ │ └─ update / edition / language / sw_edition / target_sw / target_hw / other
│ │ │ │ └─ version
│ │ │ └─ product
│ │ └─ vendor
│ └─ part: 'a' (application), 'o' (OS), 'h' (hardware)
└─ CPE 2.3 spec version
Key properties:
- Centralized. A new product needs a CPE entry in the NVD dictionary before CVEs can be attached to it. NIST analysts add entries; vendors can submit them.
- Product-scoped, not package-scoped. "OpenSSL 3.0.13" gets one CPE regardless of whether you got it from apt, vcpkg, or a Yocto recipe.
- What the NVD speaks. Every CVE in the NVD has a
configurationsblock listing affected CPEs. Querying "what CVEs apply to my SBOM" through the NVD API means querying by CPE. - Lags reality. CPE entries for new packages, niche libraries, embedded RTOSs, and vendor-specific drivers often don't exist, or exist with the wrong vendor/product slug. The NVD has publicly acknowledged this backlog.
What PURL is
Package URL is a specification maintained on GitHub by the PURL community. It identifies a package by the ecosystem it came from:
pkg:npm/lodash@4.17.21
pkg:pypi/cryptography@42.0.5
pkg:maven/org.bouncycastle/[email protected]
pkg:cargo/[email protected]
pkg:golang/golang.org/x/[email protected]
pkg:deb/debian/[email protected]?arch=amd64
pkg:generic/openssl@3.0.13?download_url=https://www.openssl.org/source/openssl-3.0.13.tar.gz
Key properties:
- Decentralized. No registry. You generate the PURL from the package metadata you already have.
pkg:npm/...means "npm registry";pkg:pypi/...means "PyPI"; the resolver is implicit in the type. - Package-scoped, not product-scoped. Two PURLs from different ecosystems can describe what humans call "the same library" -
pkg:npm/openssl-bindingis different frompkg:generic/openssleven though they wrap the same C code. - What CycloneDX speaks natively. Every CycloneDX component carries a
purlfield. SPDX 2.3+ supports PURL as anexternalRefof typepackage-manager. - Built for software supply chain reality. Transitive dependencies, lockfile versions, hashed sources - PURL was designed to mirror the way real software is shipped.
The comparison table
| Dimension | CPE | PURL |
|---|---|---|
| Maintainer | NIST (centralized) | PURL community / GitHub (decentralized) |
| Authority needed? | Yes - NIST adds entries | No - generated from package metadata |
| Scope | Products (OS, app, hardware) | Packages (npm, PyPI, Maven, Go, Cargo, Deb, RPM, etc.) |
| Format | 13-field colon-delimited string | URL-like pkg:type/namespace/name@version?qualifiers |
| Native in | NVD, MITRE CVE records, many enterprise scanners | CycloneDX 1.4+, SPDX 2.3 (as externalRef), most open-source SCA tools |
| Vulnerability data source | NVD configurations[] block on every CVE |
OSV.dev, GitHub Advisory Database, ecosystem-native feeds |
| Coverage of OSS libraries | Partial - NVD backlog, missing niche/embedded libs | Near-complete for any package in a real package manager |
| Coverage of commercial / embedded / firmware | Strong for big-name vendors (Microsoft, Cisco, Siemens) | Weak - no PURL type for most embedded RTOS / vendor binaries |
| Versioning | Pinned exact version per CPE; ranges expressed in CVE config | Exact version in the PURL string |
| Best for | Querying NVD for CVEs; commercial / OS / hardware components | Open-source dependencies; build-time SBOM generation |
| FDA acceptance | Accepted under 2026 premarket guidance | Accepted under 2026 premarket guidance |
Why medical device SBOMs end up needing both
The FDA's February 2026 final guidance requires that every component in an SBOM carry a "unique identifier" and explicitly names PURL and CPE as acceptable. That phrasing is permissive - but the operational reality of running a device program for ten years post-clearance is not.
Premarket (build-time) leans PURL. Your CycloneDX or SPDX SBOM is generated by tooling - Syft, cdxgen, Microsoft's SBOM tool, your build system - that parses lockfiles, scans binaries, and emits PURLs. This is where 80–95% of components in a typical SaMD or connected-device SBOM live: open-source libraries shipped through npm, PyPI, Maven, NuGet, Cargo, apt, dnf, or pulled into a Yocto/Buildroot image.
Postmarket (monitoring) leans CPE. Once the device is in the field, you need a feed of "new CVEs that affect components on this device." The NVD is the canonical CVE source. NVD CVEs are indexed by CPE. Ecosystem-native feeds (OSV.dev, GHSA) cover PURLs well for open-source - but the NVD is still where most automated vulnerability tooling, government threat feeds, and CISA KEV cross-references resolve. You need CPE coverage to talk to that ecosystem.
Commercial, embedded, and firmware components live in CPE territory. An RTOS (FreeRTOS, VxWorks, QNX, embOS), a Bluetooth stack, a TCP/IP stack (lwIP, Treck), a chipset driver, a commercial database, the operating system on your gateway - these typically have CPE entries but no meaningful PURL. If you ship a class-II device with a connected hub, that hub's OS and firmware identifiers are CPE-only in practice.
Result: a mature medical device SBOM carries PURLs for everything generated from a package manager, CPEs for OS/firmware/commercial components, and - critically - a CPE for the device itself as a product so future CVEs can be attached to it by researchers.
How to map PURL to CPE (and where it breaks)
There is no official PURL ↔ CPE mapping. Several open-source projects try:
- purl2cpe - community mapping database
- OWASP Dependency-Track - maintains its own internal mapping when querying the NVD
- Anchore Grype, Trivy, Syft - each ships a vulnerability matcher that bridges PURL → CPE for NVD queries plus PURL → OSV for ecosystem-native queries
- CycloneDX SBOM tools - increasingly emit both
purlandcpefields on the same component when a mapping is known
Where it breaks in real medical device programs:
- Vendor/product slug ambiguity.
pkg:pypi/cryptographymaps tocpe:2.3:a:pyca:cryptography:*- but only if your tooling knows that. Pick the wrong vendor slug and your NVD query returns nothing. - Statically-linked C libraries discovered by binary analysis. A binary SBOM tool emits
pkg:generic/openssl@3.0.13because there's no package manager involved. Mappingpkg:generic/...to a CPE is best-effort. - Forked or vendored libraries. Your team forked
lwIPtwo years ago and ships a modified copy. The PURL points to a git repo that's not in any package manager; the CPE points to upstream lwIP CVEs that may or may not apply to your fork. - Vendor-supplied drivers and SDKs. A chipset SDK from a silicon vendor has no PURL and no CPE. It's a tarball with a version number. You have to either get the vendor to register a CPE or accept that postmarket CVE monitoring for that component is manual.
- NVD analyst lag. A new CVE published last week may not have its CPE configuration block populated yet. PURL-native feeds (OSV, GHSA) often see it first.
The practical rule: never rely on a single identifier or a single feed. Run NVD-by-CPE and OSV-by-PURL and CISA KEV and vendor PSIRT feeds in parallel, deduplicate hits, and triage every hit with a VEX statement.
FDA postmarket implications
Section 524B requires "a plan to monitor, identify, and address postmarket cybersecurity vulnerabilities and exploits in a reasonable time." The FDA's 2025 postmarket guidance and the February 2026 premarket guidance both make clear that "identify" means a continuous, machine-readable matching process between your shipped SBOM and live vulnerability feeds.
Reviewers and inspectors increasingly ask three operational questions about identifier hygiene:
- "Show me a component on the device that does not have a CPE." If your answer is "we monitor it through PURL on OSV.dev," that's acceptable - but you need the documented process that shows you're doing it.
- "Show me how you handle a CVE that's published to the NVD but doesn't yet have a CPE configuration." This is increasingly common given the NVD backlog. Your runbook needs to address it.
- "Show me the VEX statement for the last three CVEs your SBOM matched." VEX is how you tell reviewers, customers, and CISA whether an apparent vulnerability is actually exploitable in your device's intended-use context.
The identifier choice is upstream of all three. If your SBOM components carry both PURL and CPE wherever possible, you can answer those questions credibly. If your SBOM is PURL-only or CPE-only, you have to explain why - and the explanation has to be defensible.
Practical guidance for medical device teams
- Generate your build-time SBOM in CycloneDX 1.5+ with PURLs for everything sourceable from a package manager. This is what CycloneDX is good at and what your build tooling will produce automatically.
- Add CPE for OS, firmware, RTOS, commercial libraries, vendor drivers, and the device itself. Use the NVD CPE Dictionary to find the right strings. Register a CPE for your device under your company's vendor slug.
- Where both exist, ship both. CycloneDX lets you emit
purlandcpeon the same component. Do it. - Run multi-feed monitoring. NVD (CPE), OSV.dev (PURL), GitHub Advisory Database (PURL), CISA KEV (CPE-keyed), and vendor PSIRT feeds for anything commercial. Deduplicate on CVE ID.
- Triage every hit with VEX. A CVE match without a VEX statement is just noise on a reviewer's desk. See VEX Documents for Medical Devices for the workflow.
- Document your identifier strategy in your postmarket cybersecurity plan. Reviewers want to see that you've thought about this - not that you happened to land on PURLs because your tooling did.
FAQ
Does the FDA prefer CPE or PURL?
Neither. The February 2026 final premarket cybersecurity guidance accepts both and treats "unique identifier" as the requirement. What reviewers actually flag is SBOMs where components have no identifier at all, or identifiers that don't resolve to anything queryable.
Can I get away with PURL only?
For SaMD that's pure open-source dependencies on top of a managed cloud platform, mostly yes - OSV.dev and GHSA cover the PURL-native side well. For any connected device, embedded device, or device with an OS/firmware layer, no. You'll miss OS and commercial CVEs.
Can I get away with CPE only?
Almost never. Modern open-source dependency trees have thousands of components, and a significant fraction don't have CPE entries. You'll miss OSS CVEs.
Who creates a CPE for my device?
You can submit one to NIST through the CPE Dictionary submission process. Most medical device manufacturers don't, which is one reason researcher-disclosed vulnerabilities on devices end up with non-standard or missing CPE references.
How does VEX fit in?
VEX (Vulnerability Exploitability eXchange) is the layer above identifiers - it's how you state whether a matched CVE is actually exploitable. Without VEX, you generate an unbounded triage queue. See VEX Documents for Medical Devices.
Does CycloneDX or SPDX handle PURL/CPE better?
CycloneDX has a first-class purl field on every component and a cpe field alongside it; both are part of the core schema. SPDX 2.3 supports PURL and CPE as externalRefs. Functionally either format works, but CycloneDX is more ergonomic for dual-identifier components. See CycloneDX vs SPDX for the broader format comparison.
Related guides
- SBOM Vulnerability Management for Medical Devices
- VEX Documents for Medical Devices and the FDA
- CycloneDX vs SPDX: Choosing an SBOM Format for the FDA
- FDA Premarket Cybersecurity Submission Checklist
- FDA Section 524B Cybersecurity Requirements Explained
References
- FDA, "Cybersecurity in Medical Devices: Quality System Considerations and Content of Premarket Submissions," final guidance, February 2026
- Section 524B of the Federal Food, Drug, and Cosmetic Act
- NIST, "Official Common Platform Enumeration (CPE) Dictionary," nvd.nist.gov/products/cpe
- PURL Specification, github.com/package-url/purl-spec
- CycloneDX 1.6 Specification, cyclonedx.org/specification
- SPDX 2.3 Specification, spdx.github.io/spdx-spec
- OSV.dev, "A distributed vulnerability database for open source," osv.dev
- CISA, "Known Exploited Vulnerabilities Catalog," cisa.gov/known-exploited-vulnerabilities-catalog
Sources & references
Primary sources cited in this article. Links open in a new tab.