> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pharussecurity.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SBOMs

> Send CycloneDX or SPDX SBOMs to Pharus by upload, from CI or from Dependency-Track, and see what Pharus read from each one.

<Info>
  **Plans:** Every plan. On Free, your SBOMs stay readable and downloadable, but new ones are refused. **Who:** any member uploads and retires SBOMs; owners and admins create the API tokens that pipelines use.
</Info>

An SBOM (software bill of materials) lists the components in one version of your product. Pharus reads every SBOM you send, matches its components against known and exploited vulnerabilities, and keeps the original file. Each SBOM belongs to one [product family](/monitoring/product-families).

## Supported formats

| Format | Versions | Encoding |
| - | - | - |
| CycloneDX | 1.4, 1.5, 1.6, 1.7 | JSON |
| SPDX | 2.2, 2.3 | JSON |

Pharus doesn't read XML SBOMs (CycloneDX XML, SPDX RDF/XML), SPDX tag-value or SPDX 3 (JSON-LD). Export JSON instead, for example with `syft -o cyclonedx-json` or `cyclonedx convert --output-format json`.

## Size limits

* At most 4 MB as sent. Gzip the file to stay under it: the app does this for you, and the pipeline examples do it with `gzip`.
* At most 50 MB uncompressed.
* At most 50 000 components.

## How SBOMs arrive

| Way | Best for | How |
| - | - | - |
| Upload in the app | A first SBOM, or products without a pipeline | **SBOMs → Upload SBOM**, below |
| The API, from CI | Every release, automatically | [GitHub Actions](/api-reference/ci/github-actions), [Azure Pipelines](/api-reference/ci/azure-pipelines), [GitLab CI](/api-reference/ci/gitlab-ci) or any [HTTP client](/api-reference/sboms) |
| Dependency-Track 4.11 and later 4.x | Teams that already upload BOMs to Dependency-Track | A notification alert that forwards each processed BOM. See [Dependency-Track](/api-reference/dependency-track). |
| sbomify | Teams that keep SBOMs in sbomify | sbomify can't send SBOMs on by itself, so your CI does: a step after sbomify-action, or a nightly job that fetches the newest SBOM |

The **Connect a pipeline** card on the **SBOMs** page has ready-made snippets with your endpoint filled in, in tabs for **GitHub Actions**, **Azure DevOps**, **Dependency-Track**, **sbomify** and **curl**.

## Upload an SBOM

<Steps>
  <Step title="Open the upload dialog">
    Go to **SBOMs** and select **Upload SBOM**, or **Upload SBOM** on a family's card.
  </Step>

  <Step title="Choose the family and version">
    Pick the **Product family**. Enter the **Product version**, or leave it empty to use the version in the SBOM.
  </Step>

  <Step title="Choose the file">
    Drop a `.json` file on **SBOM file**, or select **Choose file**. Your browser compresses it before it is sent.
  </Step>

  <Step title="Upload">
    Select **Upload SBOM**. Pharus confirms with the number of components it read, and matching starts.
  </Step>
</Steps>

If you send a file Pharus already has for that family, it says "This SBOM was already uploaded" and stores nothing new.

### Try the sample SBOM

Before your first SBOM, the **SBOMs** page offers **Try a sample SBOM**. It uploads a sample firmware SBOM, `gateway-g3-fw-4.2.cdx.json` (CycloneDX 1.5, 19 components, including log4j-core 2.14.1, which is in CISA KEV), to the family you choose, so you can see matching, the KEV alert and an incident end to end.

<Note>
  The sample works like a real SBOM: the KEV match opens a real incident and sends the alert emails. When you're done, retire the sample SBOM and close the incident.
</Note>

## What happens after an SBOM arrives

<Steps>
  <Step title="Pharus stores and reads it">
    Pharus keeps the original file and reads the components: name, version, purl, CPE and supplier. It leaves out the product itself and, in CycloneDX 1.7, external components. A component listed twice counts once.
  </Step>

  <Step title="It settles the product version">
    The version you gave (in the upload dialog or the API request) wins. Without one, Pharus uses the product version in the SBOM: `metadata.component` in CycloneDX, or the package the document describes in SPDX. Without either, the SBOM shows as "No version".
  </Step>

  <Step title="It replaces the older SBOM of the same version">
    A family has one active SBOM per product version. A new SBOM for version 4.2 retires the previous 4.2 SBOM; SBOMs of other versions stay active. The family's current version becomes the new version if it is the highest so far.
  </Step>

  <Step title="It matches the components">
    Within seconds Pharus matches the family's components against OSV.dev, CISA KEV and ENISA EUVD. Results appear under [Vulnerabilities](/monitoring/vulnerabilities), and an exploited match can open an incident by itself.
  </Step>
</Steps>

Each SBOM has a status:

| Status | Meaning |
| - | - |
| Received | Stored, not yet read |
| Parsed | Read; matching against CISA KEV, EUVD and OSV |
| Matched | Matched against CISA KEV, EUVD and OSV |
| Failed | Matching failed; Pharus retries on the next run |

### Get exact matches

Pharus matches a component exactly when it has a purl of a type that OSV.dev covers: `npm`, `pypi`, `maven`, `golang`, `cargo`, `nuget`, `gem`, `composer`, `hex`, `pub`, `deb`, `apk`, `swift`, `cran` or `hackage`. It ignores purl qualifiers and subpaths, except `distro` on Debian packages: give it (for example `distro=bookworm` or `distro=debian-12`), or every Debian release's open vulnerabilities match.

Components without such a purl (CPE only, `generic`, `conan`, `rpm` and others) are only compared by name against the exploited lists, so their version isn't checked. See [how matching works](/monitoring/vulnerabilities#how-matching-works).

## The SBOMs page

**SBOMs** shows one card per active product family with its **Current version**, **Components**, **Last SBOM** and the number of active versions. The table lists the family's active SBOMs, newest first:

| Column | What it shows |
| - | - |
| **Version** | The product version; select it to open the SBOM |
| **Format** | CycloneDX or SPDX, with the spec version |
| **Source** | Upload, GitHub Actions, Azure DevOps, Dependency-Track, sbomify or API |
| **Received** | When Pharus received it, in Oslo time |
| **Components** | Components in this SBOM |
| **Status** | Received, Parsed, Matched or Failed |

Retired SBOMs leave this list. [Search](/monitoring/vulnerabilities#search) still finds them under **SBOM versions**.

## An SBOM's page

* **Details**: **Status**, **Product family**, **Product version**, **Product in SBOM**, **Format**, **Serial number**, **SHA-256** and size, **Source**, **Sent by** (the member, or the API token's name), and when it was **Received**, **Parsed** and **Matched**.
* **Pipeline**: **Repository**, **Branch**, **Commit** and a link to the **Run**, when the pipeline sent them with the `X-Pharus-Repo`, `X-Pharus-Branch`, `X-Pharus-Commit` and `X-Pharus-Run-Url` headers. For Dependency-Track, the repository is the Dependency-Track project's name.
* **Components**: every component with **Name**, **Version**, **purl / CPE**, **Type** and, when there are any, **Vulnerabilities** with the number exploited. Use **Filter by name, version or purl** to narrow the list; it shows up to 500 rows at a time.

### Download the original

Select **Download original** on an SBOM's page. You get the file exactly as Pharus received it, uncompressed, named after the family and version, for example `gateway-g3-4.2.cdx.json`. Every member can download it, also while the workspace is read-only. The original files are also part of the company's data export.

## Retire an SBOM

Retire an SBOM when that version is no longer on the market or in the field.

<Steps>
  <Step title="Open the SBOM">
    On **SBOMs**, select the version.
  </Step>

  <Step title="Retire it">
    Select **Retire SBOM**, then **Retire SBOM** again to confirm.
  </Step>
</Steps>

Its components stop counting towards the family and new matches aren't raised for it. The SBOM itself and its history stay in the audit trail, and its page says when it was retired.

<Warning>
  Retiring can't be undone. Sending the same file again doesn't bring it back, because Pharus treats it as a copy of the SBOM it already has. Send a regenerated SBOM instead.
</Warning>

## Related

<CardGroup cols={2}>
  <Card title="Vulnerabilities" icon="bug" href="/monitoring/vulnerabilities" />

  <Card title="Upload SBOMs with the API" icon="upload" href="/api-reference/sboms" />

  <Card title="Dependency-Track" icon="webhook" href="/api-reference/dependency-track" />

  <Card title="Product families" icon="cpu" href="/monitoring/product-families" />
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.