# How to Scan Python Dependencies for Vulnerabilities


Every dependency in a Python project is a potential source of known security vulnerabilities. Scanning those dependencies against a vulnerability database catches problems before they reach production.

## Using uv audit

[uv](https://pydevtools.com/handbook/reference/uv.md) 0.10.12 and later includes the `uv audit` command, which checks project dependencies against the [OSV](https://osv.dev/) (Open Source Vulnerabilities) database.

Run it from the root of a uv project:

```console
$ uv audit
```

`uv audit` reads the project's lockfile and queries OSV for known vulnerabilities in each dependency. When vulnerabilities are found, it prints details with links to the relevant advisories and exits with a non-zero status code. When no vulnerabilities are found, it exits with status 0.

To emit the findings as JSON for scripts or dashboards instead of human-readable text, pass `--output-format json`.

To point `uv audit` at a custom vulnerability service instead of OSV, use the `--service-url` and `--service-format` flags:

```console
$ uv audit --service-url https://vuln.example.com/api --service-format osv
```

> [!NOTE]
> `uv audit` is in preview, and each run prints a warning that its interface may change; pass `--preview-features audit` to silence it. The base command requires uv 0.10.12 or later. Ignoring findings requires 0.11.3, adverse status reporting 0.11.9, and JSON output 0.11.15. Run `uv self version` to check, and `uv self update` to upgrade.

## Ignoring a vulnerability

Not every finding is actionable: a fix may not exist yet, or the vulnerable code path may be unreachable from the project. Suppress a finding by passing its advisory ID. `--ignore` suppresses it unconditionally; `--ignore-until-fixed` suppresses it only while no fixed release exists, so the audit fails again once a patch is published.

```console
$ uv audit --ignore CVE-2021-33503 --ignore-until-fixed GHSA-2xpw-w6gg-jr37
```

Both flags can be repeated, and both accept any alias for the advisory (CVE, GHSA, PYSEC, or OSV IDs). Ignoring by an alias suppresses every advisory record that shares it.

To persist ignores so CI runs apply them too, add a `[tool.uv.audit]` section:

```toml
# pyproject.toml
[tool.uv.audit]
ignore = ["CVE-2021-33503"]
ignore-until-fixed = ["GHSA-2xpw-w6gg-jr37"]
```

If an ignored ID matches no finding in the project, `uv audit` prints a warning, which catches typos and entries that have outlived the vulnerable dependency.

## Catching deprecated and archived dependencies

`uv audit` also reports adverse project statuses. PyPI's project status markers (defined by [PEP 792](https://peps.python.org/pep-0792/)) let maintainers mark a project as `archived` (no further releases expected), `deprecated` (obsolete by its maintainers' own judgment), or `quarantined` (locked by PyPI admins as unsafe). Dependencies carrying one of these statuses appear in the audit report:

```console
$ uv audit
Resolved 2 packages in 1ms
Found no known vulnerabilities and 1 adverse project status in 1 package

Adverse statuses:

- pathlib is archived
```

An archived or deprecated dependency still installs and runs, but it will not receive security fixes. Treat the finding as a prompt to plan a replacement.

## Auditing a self-contained script

To audit a single-file script with [PEP 723 inline metadata](https://pydevtools.com/handbook/explanation/what-is-pep-723.md) instead of a project, pass `--script`:

```console
$ uv audit --script demo.py
```

uv resolves the script's declared dependencies and checks them against the same vulnerability database. No `uv.lock` is required.

## Using pip-audit

[pip-audit](https://github.com/pypa/pip-audit) is an established alternative that also queries the OSV database. It works with any Python project, regardless of whether the project uses uv.

Run it as a one-off tool with `uvx`:

```console
$ uvx pip-audit
```

This scans the packages installed in the current environment. To scan a `requirements.txt` file instead:

```console
$ uvx pip-audit -r requirements.txt
```

Like `uv audit`, pip-audit exits with a non-zero status code when vulnerabilities are found.

## Adding vulnerability scanning to CI

Both tools work well in GitHub Actions because they return non-zero exit codes on findings, which fails the CI step.

Here is a GitHub Actions workflow that runs `uv audit` on every push and pull request:

```yaml
name: Vulnerability scan
on: [push, pull_request]

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v8.2.0
      - run: uv audit
```

To use pip-audit instead (for example, if the project uses an older version of uv or does not use uv at all):

```yaml
name: Vulnerability scan
on: [push, pull_request]

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v8.2.0
      - run: uvx pip-audit -r requirements.txt
```

## Learn more

- [How to Protect Against Python Supply Chain Attacks with uv](https://pydevtools.com/handbook/how-to/how-to-protect-against-python-supply-chain-attacks-with-uv.md) covers dependency cooldowns and uv's install-time malware checks.
- [uv audit CLI reference](https://docs.astral.sh/uv/reference/cli/#uv-audit)
- [pip-audit documentation](https://github.com/pypa/pip-audit)
- [OSV database](https://osv.dev/)
