Blue Goat CyberSMMedical Device Cybersecurity
    K
    Guide · Standards

    CPE vs PURL for Medical Device SBOMs: Which Identifier and When

    How CPE and PURL identifiers differ, why medical device SBOMs need both, and how to map PURL to CPE for FDA postmarket CVE monitoring under Section 524B.

    Hero illustration for the Standards article: CPE vs PURL for Medical Device SBOMs: Which Identifier and When
    Christian Espinosa, Founder & CEO at Blue Goat Cyber

    By Christian Espinosa, MBA, CISSP

    Founder & CEO · Blue Goat Cyber

    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 configurations block 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-binding is different from pkg:generic/openssl even though they wrap the same C code.
    • What CycloneDX speaks natively. Every CycloneDX component carries a purl field. SPDX 2.3+ supports PURL as an externalRef of type package-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 purl and cpe fields on the same component when a mapping is known

    Where it breaks in real medical device programs:

    1. Vendor/product slug ambiguity. pkg:pypi/cryptography maps to cpe:2.3:a:pyca:cryptography:* - but only if your tooling knows that. Pick the wrong vendor slug and your NVD query returns nothing.
    2. Statically-linked C libraries discovered by binary analysis. A binary SBOM tool emits pkg:generic/openssl@3.0.13 because there's no package manager involved. Mapping pkg:generic/... to a CPE is best-effort.
    3. Forked or vendored libraries. Your team forked lwIP two 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.
    4. 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.
    5. 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:

    1. "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.
    2. "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.
    3. "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

    1. 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.
    2. 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.
    3. Where both exist, ship both. CycloneDX lets you emit purl and cpe on the same component. Do it.
    4. 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.
    5. 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.
    6. 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.

    References

    Sources & references

    Primary sources cited in this article. Links open in a new tab.

    1. official CPE Dictionary- NIST
    2. publicly acknowledged this backlog- NIST
    3. NVD CPE Dictionary- NIST
    4. cisa.gov/known-exploited-vulnerabilities-catalog- CISA
    Ready when you are

    Get FDA cleared without the cybersecurity headaches.

    30-minute strategy session. No cost, no commitment - just answers from people who've shipped 250+ FDA submissions.