Skip to main content

Managing the SBOM Registry

A Software Bill of Materials (SBOM) is a foundational element of modern supply chain security and a core requirement for compliance under the Cyber Resilience Act (CRA). The CVD Portal features a comprehensive SBOM Registry, providing a centralized repository for tracking the third-party libraries, open-source components, and proprietary code that make up your software products.

The registry allows you to import standard SBOM formats (such as SPDX and CycloneDX) generated by your build pipelines. Once ingested, you can run an on-demand CVE scan from any submission detail page - the portal correlates each registered component against the Global CVE Allocation System, which indexes 79 upstream vulnerability sources, and surfaces matching vulnerabilities with CVSS scores, EPSS probabilities and exploitation evidence. The Supply Chain Intelligence page also checks every SBOM component by exact version. A component with a package URL is checked against OSV.dev. A component with only a CPE that includes the version, for example firmware or a vendor binary, is checked against the CVE data that CVD Portal keeps. See How CPE components are checked. A CPE with a wildcard version is not checked.

Maintaining an accurate and up-to-date SBOM Registry is essential for rapid incident response. When a major vulnerability such as Log4Shell is disclosed, the exposure search shows which of your products are affected and which component is the cause. This comprehensive visibility is crucial for demonstrating control over your software supply chain and fulfilling regulatory obligations regarding component transparency.

Releases and the SBOM archive​

You can record releases of each product and store the component list of each release. Open Products, select a product, and choose Releases and SBOM archive.

The registry keeps one SBOM for each release. The newest release also sets the current SBOM of the product. An upload for an older release goes to the archive only and does not replace the current SBOM. This stays true when uploads for two releases of one product arrive at the same time. The portal stores them one after the other, so the newest release always ends as the current SBOM.

The system picks the newest release using the latest release date. When a release has no release date, the creation date is used instead. A withdrawn release is never the newest.

Each release can specify its own support end date. When a release has no support end date, the support period of the product applies.

The continuous integration endpoint accepts releaseVersion and the optional releasedAt timestamp, together with productId. releasedAt takes a date such as 2026-09-30 or an ISO 8601 timestamp with an offset. A date that does not exist, for example 2026-02-30, is refused. For example, send:

{
"filename": "sbom.cdx.json",
"content": "...",
"productId": "...",
"releaseVersion": "2.4.1",
"releasedAt": "2026-09-30"
}

Daily scan of releases in the field​

On the Enterprise plan, the daily scan also checks the SBOM of each older release that is still in the field. A release is in the field until its support end date, or the support end date of the product when the release has none. A release with no support end date at all is scanned. A withdrawn release is scanned until its support ends, because units that customers already have stay in the field. A release with the same components as the current SBOM, usually the newest release, is covered by the current SBOM, so the scan does not check it twice. When the scan cannot check every release in one run, the release with the oldest scan goes first in the next run.

Each release keeps its own list of findings. When a newer release upgrades a component, the finding stays open on the older releases that still ship the old version.

  • The first scan of a release records its findings without an alert. Later scans alert on new findings in the daily email and with the threat_intel.match webhook.
  • A finding that is already open on the current SBOM does not alert again for a release.
  • The Releases page of the product shows the open findings of each release in the Known vulnerabilities column.
  • The exploitation watch also checks the findings of these releases until their support ends.

The exposure search still checks the current SBOM only.

Parts of one product SBOM​

A product can have several SBOM files, for example one for the firmware and one for each back-end application that the device calls. Upload each file as a named part of the product SBOM. The CRA treats them as one SBOM of one product, and the portal uses all parts together.

  • Give the part a name when you upload, for example firmware or device-api. A name has 1 to 40 lowercase letters, digits or hyphens.
  • An upload with a part name replaces only that part. The other parts stay.
  • An upload with no part name replaces the whole SBOM of the product, with all its parts. This is what an upload did before parts existed, so existing pipelines keep working.
  • A product can have up to 25 parts. All parts together can list up to 10,000 unique components.
  • Parts are available on a product. The All products SBOM stays one file, and an upload there with a part name gets HTTP 400.

The vulnerability scan, the findings, the exposure search and the technical file use the union of all parts. A component that two parts ship is stored once. Each finding, each exposure search result and each review trigger names the parts that ship its component. The refresh date of the SBOM is the oldest upload of its parts, so one part that nobody updates makes the whole SBOM stale.

To remove one part, choose Remove part in the parts list. When you remove the last part, the SBOM of the product is removed, and the product falls back to the All products SBOM.

The CycloneDX download on the trust portal is hierarchical when a product has two or more parts. The product is the top component, each part is a component under it, and the components of each part sit inside that part.

A release upload with a part name stores a copy of all parts as they are at that release. An upload for an older release changes only the copy of that release.

The CI endpoint and the MCP tool upload_sbom take the same optional part field. See the pipeline connectors.

Which product an SBOM covers​

An SBOM names a product. Annex VII point 8 of the CRA files the SBOM in a product's technical documentation and requires it to be produced to market surveillance authorities on reasoned request, so the obligation belongs to the product rather than to the manufacturer.

You do not have to pick a product to upload one. Leaving the scope set to All products stores an account-wide SBOM, and every product that has none of its own uses it. If you ship one thing, or one component list genuinely describes everything you ship, this is the right answer and nothing else needs doing.

Uploading an SBOM for a specific product overrides the account-wide one for that product alone. Other products carry on using the account-wide list. On a product's Evidence tab, the SBOM card names which of the two is in force, so an inherited component list is never mistaken for one written for that product.

Two consequences worth knowing:

  • Removing a product's own SBOM does not leave it bare. It falls back to the account-wide one if there is one, and the technical file is rewritten to describe that instead.
  • Advisory findings are attributed to the SBOM that produced them. A finding from a product's own SBOM raises a review trigger on that product only. A finding from the account-wide SBOM raises a review trigger on each product that has no SBOM of its own. A product with its own SBOM gets no trigger from the account-wide SBOM.

Whichever scope you upload at, the SBOM is written into the technical file of every product it covers, and satisfies Annex VII point 8 there. Until a product has one, its Annex VII export is blocked, and the Evidence tab says so.

The CI endpoint takes an optional productId for the same purpose. Omitting it pushes to the account-wide SBOM, which is what existing pipelines already do and what they keep doing. A monorepo that builds several products pushes each one by id. See the API overview.

The GitHub connector writes the account-wide SBOM, since a connection names one repository. A product with its own SBOM keeps overriding it.

One product with firmware and back-end applications​

A device often has several SBOM files, for example one for the firmware and one for each back-end application that the device calls. Upload each file to the product as a named part, such as firmware, device-api and ota-service. See Parts of one product SBOM.

The CRA makes the remote data processing solutions of a device part of the product (Article 3(1)). A back-end application is a remote data processing solution when the device or its app needs it for a function and you designed or developed it (Article 3(2)). Back-end systems that the device does not call directly, such as billing or analytics behind the device API, stay out of the product SBOM.

Do not upload the firmware SBOM to the product and the back-end SBOM to All products. The product SBOM replaces the account-wide SBOM for that product, so the technical file and the exposure search of the product then show the firmware components only. Upload both as parts of the product. When several devices call one back end, upload the back-end SBOM as a part of each device.

A file that is already a hierarchical CycloneDX merge (cyclonedx merge --hierarchical) also works. Upload it with no part name, and the portal reads every nested component.

Each upload takes one JSON file of up to 5 MB. All parts together can list up to 10,000 unique components. The blog article How many SBOMs does a CRA product need? explains the legal basis and the pipeline set-up.

Keeping the registry current​

One SBOM is stored per product, plus the account-wide one, and every upload replaces the SBOM at that scope. The registry shows when each was last refreshed, and flags any that is more than 90 days old. The date is the last upload that replaced the components. A vulnerability scan does not change it. The control register runs the same 90-day check on a schedule, against the oldest SBOM in the account, so one actively maintained product cannot mask a neglected one.

The reason to act on that flag is that correlation only knows the versions your stored SBOM pins. Upgrade a component and the old advisories keep matching until you re-upload, so a registry left alone for a quarter accumulates findings against versions you no longer ship. Those are not false positives about the component, they are true statements about a stale snapshot, which is why the fix is a re-upload rather than dismissing them one by one.

Once you do re-upload, the next scan closes the findings that no longer apply. Each scan reconciles the stored findings against the SBOM as it stands, stamping a resolution date on anything the current component set no longer matches, so the supply-chain checks in the control register recover on their own after an upgrade. Resolved findings are kept, not deleted, and stay visible as history on the supply-chain page. A finding whose vulnerable version returns to the SBOM reopens and alerts again.

There are two ways to avoid the problem rather than manage it. Pushing the SBOM from your build pipeline on every release makes the freshness clock track your release cadence instead of anyone's memory, and the API overview has the endpoint. Where no such pipeline step exists, connecting GitHub has the portal read your repository's dependency graph every night and replace the account-wide SBOM itself.

How component matching works​

Correlation runs against GCVE, which indexes vulnerabilities by CPE vendor and product across 79 upstream sources. Where your SBOM declares a CPE for a component (CycloneDX cpe, SPDX cpe23Type), the portal uses it directly, which makes the match a lookup rather than a name guess. Where it does not, the component name is mapped onto a vendor and product heuristically. Generating SBOMs that include CPEs alongside package URLs measurably improves match quality.

A package that appears at more than one version is registered at each of them, and scanned at each of them. This matters because a direct dependency is often patched while an older copy of the same package survives deeper in the tree, and only the older copy is vulnerable. The same name in two ecosystems, requests on npm and requests on PyPI for example, is likewise kept apart, since they are unrelated packages.

Each match carries its exploitation context: EPSS score, KEV catalogue assertions, and sighting counts. See Exploitation Signals.

On the Enterprise plan, the nightly scan sends up to 10,000 components to OSV.dev, the same number an SBOM can list. A scan that runs inside a request, such as an upload or a push from CI or MCP, stops at 2,048 components, so that the request stays short. When an SBOM lists more, that scan counts as not complete. On the Enterprise plan the nightly scan checks the rest. On other plans no scan can check the whole SBOM, and the technical file says so.

How CPE components are checked​

Firmware SBOMs often name their parts by CPE and give no package URL, for example BusyBox, OpenSSL or the Linux kernel of a custom image. The Supply Chain Intelligence scan checks these components against a copy of the CVE data that CVD Portal keeps.

The copy holds two sources and is updated every 2 hours.

  • The NVD configurations, which name the vulnerable product by CPE with version ranges.
  • The affected versions that the vendor or the CVE Numbering Authority (CNA) wrote into each CVE record of the CVE List.

NIST stopped routine NVD analysis on 15 April 2026, so most CVEs published after that date have only the CNA data. When the CNA record states whether your version is affected, the CNA record decides, because the CNA owns the record. For the Linux kernel this removes NVD ranges that ignore a fix in a stable branch. When the CNA record says nothing about your version, the NVD configuration decides.

The scan checks every CPE component in the SBOM. Components that share a CPE, such as the kernel modules of one image, are checked once. Each finding shows its EUVD identifier and links to the EU Vulnerability Database of ENISA when ENISA lists the CVE.

The portal reads CPE 2.3 and the older CPE 2.2 form, for example cpe:/a:busybox:busybox:1.37.0. Build systems such as OpenWrt still write CPE 2.2. A component with neither a package URL nor a CPE stays unchecked, and the scan counts it as skipped.

When the copy is not loaded or is more than 24 hours behind, the scan uses the live NVD API until the copy is current again. The live API takes 20 CPEs in each scan. A larger firmware SBOM is then only partly checked, and the portal does not count that scan as a complete check.

Component-level VEX statements​

A manufacturer can declare that a component vulnerability does not affect their product. This follows the VEX model in CSAF 2.0. It supports the manufacturer's duty to give users vulnerability information under CRA Annex I Part II and Article 14(8).

Four VEX status values are available.

  • not_affected
  • affected
  • fixed
  • under_investigation

When you set the status to not_affected, you must provide a justification or an impact statement. You can also provide both. The five approved CSAF 2.0 justification labels are available.

  • component_not_present
  • vulnerable_code_not_present
  • vulnerable_code_not_in_execute_path
  • vulnerable_code_cannot_be_controlled_by_adversary
  • inline_mitigations_already_exist

A finding with a justified not_affected status stops counting as an open exposure in automated control tests and partner exchanges. The finding remains in the immutable audit ledger. On the Enterprise plan, the daily exploitation watch still checks that vulnerability. If exploitation evidence escalates, it alerts your alert recipients and marks the conflict with the VEX statement.

VEX statements in the uploaded SBOM​

A CycloneDX SBOM can carry VEX statements in its vulnerabilities array. The scan reads them and records each statement on the matching finding.

CycloneDX analysis stateVEX status on the findingFinding state
not_affected, false_positivenot_affectedOpen
resolved, resolved_with_pedigreefixedResolved
exploitable, in_triageNo changeOpen, and alerted as usual

The CycloneDX justification becomes the nearest CSAF 2.0 label. For example, code_not_reachable becomes vulnerable_code_not_in_execute_path, and protected_at_runtime becomes inline_mitigations_already_exist. The impact statement records that the claim came from the SBOM. The product CSAF VEX document then lists the finding as known_not_affected. It does not list it as fixed.

A statement that you set by hand always wins. The scan never changes a VEX status that an administrator set in the SBOM Registry view. A statement that came from the SBOM goes away when a later upload no longer contains it. If you clear a statement by hand, the next scan applies the statement from the SBOM again. To disagree with the SBOM, set the status to affected.

Administrators can set and edit VEX statements in the SBOM Registry view. Members have read-only access. You can download a product-level CSAF 2.0 VEX document from the SBOM Registry page.

Product status in the VEX document​

The product CSAF 2.0 VEX document gives one status for each vulnerability on the whole product. The same document is available to approved viewers on the trust portal. When several components match the same vulnerability, the document combines their statements in this order.

  • Any component with the status affected makes the product known_affected.
  • Any component with the status under_investigation, or any open finding with no valid statement, makes the product under_investigation.
  • Next, any fixed or resolved finding makes the product fixed.
  • Only when every component is not_affected is the product known_not_affected.

A vulnerability with no statement on any finding is not in the document. When no vulnerability has a statement, the download is a CSAF informational advisory with no vulnerabilities list, because a VEX document needs at least one. A resolved finding counts as fixed, unless you set a statement after the resolution. A not_affected finding counts only when it has a justification label or an impact statement. Each known_affected product gets an action statement that tells users that no fix is available yet. The portal stores each version of the document. The version increases by one each time the content of the document changes, for example when you set or clear a statement or when a scan resolves a finding. It never goes down. The date of a new version is the time the portal first serves the changed document, from the dashboard or from the trust portal, and its revision entry names the vulnerabilities that changed.

A component supplier can also publish VEX statements by hand for each part number on the trust portal. The same document then has one product for each part number, and the rules above apply to each part. See VEX statements per part number.