---
title: "Known vulnerabilities"
description: "Installed plugins' packages compared with known advisories from your indexes, a file you import or a feed you turn on, with the version that fixes each one. Nothing is sent anywhere to do it."
canonical_url: "https://docs.oz0.ai/build/plugins/vulnerabilities"
---
# Known vulnerabilities

> Installed plugins' packages compared with known advisories from your indexes, a file you import or a feed you turn on, with the version that fixes each one. Nothing is sent anywhere to do it.

For: tenant administrators and plugin authors.

Inspect vulnerability findings for an installed plugin version to see which of its packages match known advisories. The edge compares the version's [SBOM](https://docs.oz0.ai/build/plugins/indexes/version-records#sboms), including lockfile packages and package closures [pinned by hash](https://docs.oz0.ai/build/plugins/packages#pinning-and-hashes), with those advisories.

Each finding identifies the affected package, advisory, severity and fixed version.

The comparison happens inside your cluster. No package list leaves it: advisories come only from places you already trust or chose.

## See the findings

```bash [Terminal]
oz0 plugin vulns
```

```text [Output]
PLUGIN  VERSION  SEVERITY  PACKAGE          ADVISORY                     FIXED IN  SOURCE
team    0.3.0    high      PyPI mcp 2.2.0   OZ0-E2E-0001 (CVE-2099-0001)  2.2.1     index:e2e

OZ0-E2E-0001: A test advisory against the locked mcp

Advisories: index:e2e 1.
```

- `oz0 plugin vulns <name>` shows one plugin; `--json` gives everything, references included.
- `oz0 plugin list` has a `VULNS` column (how many findings, and the worst severity; `-` until the first scan) and a line under the table for each affected plugin.
- On the Plugins page, an affected plugin's row has a badge, and its panel lists each finding under its SBOM, with fixed in the version to move to, or no fixed version yet.
- `oz0 doctor` warns while an installed version has a high or critical finding, and when an advisory source failed.

The tenant's readers may see their tenant's findings: they name packages and advisories, never a person or what a job did.

To fix a finding, update the plugin to a version that locks the fixed package: its publisher releases one, or, for your own plugin, run `uv lock --upgrade-package <package>` (or `npm update <package>`) and install it again.

## Where advisories come from

<table>
<thead>
  <tr>
    <th>
      Source
    </th>
    
    <th>
      How it gets in
    </th>
    
    <th>
      When it changes
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      A trusted index's <code>
        advisories
      </code>
    </td>
    
    <td>
      Signed with the index (<a href="https://docs.oz0.ai/build/plugins/indexes/manage#advisories">
        its format
      </a>
      
      ), read from the copy the cluster already keeps
    </td>
    
    <td>
      When an admin runs <code>
        oz0 index refresh
      </code>
      
       or an install reads a newer copy
    </td>
  </tr>
  
  <tr>
    <td>
      An operator's file
    </td>
    
    <td>
      <code>
        oz0 index advisories import --file FILE|DIR
      </code>
    </td>
    
    <td>
      When you import again or <code>
        oz0 index advisories remove
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      A feed you turn on
    </td>
    
    <td>
      <code>
        OZ0_ADVISORY_FEED=URL
      </code>
      
      , or <code>
        server start --advisory-feed-file FILE
      </code>
    </td>
    
    <td>
      At the edge's start and every <code>
        --advisory-feed-every
      </code>
      
       (a day)
    </td>
  </tr>
</tbody>
</table>

When two sources name the same vulnerability (by an ID or an alias they share) for the same package, the import wins over the feed, and the feed over the indexes. An entry of the import or the feed with `withdrawn` set withdraws the vulnerability from the sources after it, so you can correct what an index says without editing a signed file. The indexes count side by side: no index can hide, narrow or withdraw an advisory another index publishes, whatever their order in `oz0 index list`.

When two indexes both report one vulnerability, the finding shows the more cautious of their words (the higher severity, then no fixed version or the higher one). `oz0 index advisories list` shows each source, how many advisories it gave and skipped, and its last error. It shows who imported a file only to cluster admins; everyone else reads `operator` there, and the audit log's `advisories.import` entry records who it was.

Nothing refreshes an index for its advisories on a timer: the cluster sees new ones when the index is refreshed. To keep them current, refresh on a schedule of your own, such as a daily `oz0 index refresh` from cron.

### An operator's file

The air-gapped path. `--file` takes:

- the hub's format (`oz0-hub.advisories.v1`, below),
- an [OSV](https://ossf.github.io/osv-schema/) entry or a JSON array of them,
- OSV's bulk zip of an ecosystem (`PyPI/all.zip`, `npm/all.zip`),
- or a folder of OSV `.json` files.

At most 32 MB. PyPI and npm entries are kept; others are counted as skipped, and so is an entry that does not read or check (a bad version, a summary with a terminal escape): the import names the first five and keeps the rest, so one stray entry in an OSV zip does not cost the others.

A single OSV entry that does not check, or a file none of whose entries reads, is refused. The feed follows the same rules. An import replaces the previous one.

An [offline install](https://docs.oz0.ai/fleet/offline-install)'s folder carries `advisories/advisories.json` for this; the server never imports it by itself:

```bash [Terminal]
oz0 index advisories import --file /mnt/offline/advisories/advisories.json
```

```text [Output]
Imported 1 advisories from advisories.json (oz0-hub.advisories.v1). sha256:9c1f…
The edge scans the installed plugins against them within a minute: oz0 plugin vulns
```

Cluster admins import and remove; anyone signed in lists the sources.

### The opt-in feed

Off by default. Name an HTTPS URL of OSV entries, OSV's zip or the hub's format; the edge that holds the scanner's lease fetches the whole document (never a query naming your packages), with TLS verified, no redirect followed, at most 256 MB:

```bash [Terminal]
export OZ0_ADVISORY_FEED='https://storage.googleapis.com/osv-vulnerabilities/PyPI/all.zip'
oz0 server start ...
```

Or put the URL in a file readable only by the server's user and name it with `--advisory-feed-file FILE` (`-` reads standard input). The URL is never a flag's value: it can carry a token, and a command line is readable by every user of the machine. Logs and `index advisories list` show the URL without its query. Plain HTTP is allowed only to a loopback address, such as a mirror on the same machine. A failed fetch keeps the last advisories and is tried again within the hour.

The edge that holds the scan fetches it when it takes over the scan (at its start, so a changed URL is fetched after the restart) and then every `--advisory-feed-every`. Unset it and the feed's advisories are dropped at the next scan. It is listed on [Outbound connections](https://docs.oz0.ai/concepts/outbound-connections).

## How packages are matched

- PyPI and npm packages, from each SBOM component's package URL. Names compare as PyPI normalises them (`Typing_Extensions` is `typing-extensions`) and npm's as written.
- A version is affected when an advisory lists it exactly, or it is at or after a range's `introduced` (`0` for every version) and before its `fixed`, or at or before its `last_affected`.
- Versions compare by their dotted parts, numbers as numbers, as the hub's own checks do, so the hub and the cluster agree. Pre-releases and PEP 440's epochs and local versions compare only approximately.
- The fixed version is the lowest `fixed` above the installed version among the ranges that cover it.
- Severity is the advisory's (`low`, `moderate`, `high`, `critical`): the hub's, OSV's `database_specific.severity`, or a CVSS 3 vector's base score; `unknown` otherwise.

The edge scans a version within a minute of its install, when any source changes, and again a day after its last scan. A rescan replaces the version's findings, so an advisory gone from every source clears its finding.

## Installs

Installing or updating to a version whose packages have a known critical advisory answers with a warning naming the package, the advisory and the fixed version. A tenant may refuse such installs instead:

```bash [Terminal]
oz0 tenant vulnerable-installs acme --refuse critical --reason 'security policy 4.2'
oz0 tenant vulnerable-installs acme --refuse high --reason 'stricter after the audit'
oz0 tenant vulnerable-installs acme --refuse off --reason 'back to warnings'
```

It is a [governance setting](https://docs.oz0.ai/govern/reviewed-changes#four-eyes-for-governance-changes) (`vulnerable_installs`): every change takes a reason and is audited (`tenant.vulnerable_installs`), and refusing less (off, or `critical` after `high`) waits for another admin when four eyes is on. The default warns only, so nothing that installed before is refused.

## Who is told

A finding new to an installed version emits `plugin.vulnerable`: by email to the owner persons of the plugin's agents, flows and tools (as the [inventory](https://docs.oz0.ai/govern/inventory) names them) and to the tenant's admins, once the operator has configured email, and to every [notification rule](https://docs.oz0.ai/admin/notifications) that names the event. It names the plugin, its version, the worst severity, the advisories and the fixes; nothing about people.


## Sitemap

See the full [sitemap](https://docs.oz0.ai/sitemap.md) for all pages.
