Skip to main content

uv-audit

pip-audit like vulnerability scanning but fast

My example requirements.txt:

# This file was autogenerated by uv via the following command:
#    uv pip compile pyproject.toml -o runtime/requirements.txt
annotated-types==0.7.0
    # via pydantic
anyio==4.9.0
    # via starlette
cachetools==5.5.2
    # via google-auth
certifi==2025.4.26
    # via requests
charset-normalizer==3.4.2
    # via requests
click==8.2.1
    # via
    #   flask
    #   functions-framework
cloudevents==1.12.0
    # via functions-framework
deprecation==2.1.0
    # via cloudevents
fastapi==0.115.12
    # via availability-profile (pyproject.toml)
flask==2.2.5
    # via
    #   functions-framework
    #   vellox
functions-framework==3.8.3
    # via availability-profile (pyproject.toml)
google-api-core==2.25.0
    # via
    #   google-cloud-core
    #   google-cloud-firestore
google-auth==2.40.3
    # via
    #   google-api-core
    #   google-cloud-core
    #   google-cloud-firestore
google-cloud-core==2.4.3
    # via google-cloud-firestore
google-cloud-firestore==2.21.0
    # via availability-profile (pyproject.toml)
googleapis-common-protos==1.70.0
    # via
    #   google-api-core
    #   grpcio-status
grpcio==1.72.1
    # via
    #   google-api-core
    #   grpcio-status
grpcio-status==1.72.1
    # via google-api-core
gunicorn==23.0.0
    # via functions-framework
idna==3.10
    # via
    #   anyio
    #   requests
itsdangerous==2.2.0
    # via flask
jinja2==3.1.6
    # via flask
markupsafe==3.0.2
    # via
    #   jinja2
    #   werkzeug
packaging==25.0
    # via
    #   deprecation
    #   gunicorn
proto-plus==1.26.1
    # via
    #   google-api-core
    #   google-cloud-firestore
protobuf==6.31.1
    # via
    #   google-api-core
    #   google-cloud-firestore
    #   googleapis-common-protos
    #   grpcio-status
    #   proto-plus
pyasn1==0.6.1
    # via
    #   pyasn1-modules
    #   rsa
pyasn1-modules==0.4.2
    # via google-auth
pydantic==2.11.5
    # via fastapi
pydantic-core==2.33.2
    # via pydantic
requests==2.32.3
    # via google-api-core
rsa==4.9.1
    # via google-auth
sniffio==1.3.1
    # via anyio
starlette==0.46.2
    # via fastapi
typing-extensions==4.14.0
    # via
    #   fastapi
    #   pydantic
    #   pydantic-core
    #   typing-inspection
    #   vellox
typing-inspection==0.4.1
    # via pydantic
urllib3==2.4.0
    # via requests
vellox==0.1.3
    # via availability-profile (pyproject.toml)
watchdog==6.0.0
    # via functions-framework
werkzeug==3.1.3
    # via
    #   flask
    #   functions-framework

scanned once with pip-audit pip-audit

and once with uv-audit uv-audit

uv-audit needs 0.852s and pip-audit needs 4.617s total to scan the same requirements.txt file. I expect the time to diverge even more with larger requirements files or when using multiple requirements files.

Usage

Install:

uv pip install .

Audit a requirements file:

uv-audit -r requirements.txt

Audit a pyproject.toml (main dependencies only):

uv-audit -r pyproject.toml

Audit a uv.lock (delegated to uv audit):

uv-audit -r uv.lock

Include specific dependency groups and/or extras:

uv-audit -r pyproject.toml --group dev --extra cli

Include everything (all groups + all extras):

uv-audit -r pyproject.toml --all

Shortcut for a project directory (uses its pyproject.toml):

uv-audit ./my-project --all-groups

Mix files in one run:

uv-audit -r requirements.txt -r ./svc/pyproject.toml --all-groups

uv.lock inputs

uv.lock files are handed to uv audit --frozen, which reads the lockfile as-is and queries its own vulnerability service (OSV). No virtual environment is created and no PyPI lookups happen, so these scans are the fastest of the three.

Three consequences:

  • uv audit has no include-style selection flags, so --group, --extra, --all-groups, and --all-extras do not apply to uv.lock inputs (a warning is printed). It audits main dependencies, the default groups, and all extras.
  • uv audit lists an advisory once per identifier it is known by, so the same finding arrives as both GHSA-… and PYSEC-…. uv-audit collapses those into one row per advisory and keeps the dropped identifiers in aliases.
  • Findings come from a different advisory source than the requirements.txt/pyproject.toml path, so IDs and counts for the same project can differ between input kinds. Each finding carries its aliases so an ignore entry written as PYSEC-… still suppresses the advisory uv reports as GHSA-….

uv audit requires uv 0.12 or newer.

Machine-readable output

Use --json to emit results as JSON on stdout (errors go to stderr). The payload lists every scanned input — including clean ones — along with the resolved groups/extras selection.

uv-audit -r pyproject.toml --all --json

Example output:

{
  "vulnerable": true,
  "inputs": [
    {
      "source": "/abs/path/to/pyproject.toml",
      "kind": "pyproject",
      "groups": ["dev"],
      "extras": ["cli"],
      "vulnerabilities": [
        {
          "package": "flask",
          "version": "1.1.2",
          "id": "GHSA-XYZ",
          "fix_versions": ["2.0.0"],
          "link": "https://example.com"
        }
      ]
    },
    {
      "source": "/abs/path/to/requirements.txt",
      "kind": "requirements",
      "groups": [],
      "extras": [],
      "vulnerabilities": []
    },
    {
      "source": "/abs/path/to/uv.lock",
      "kind": "lock",
      "groups": [],
      "extras": [],
      "vulnerabilities": [
        {
          "package": "flask",
          "version": "1.1.2",
          "id": "GHSA-m2qf-hxjv-5gpq",
          "fix_versions": ["2.2.5"],
          "link": "https://example.com",
          "aliases": ["CVE-2023-30861", "PYSEC-2023-62"]
        }
      ]
    }
  ]
}

For requirements.txt and uv.lock files the groups and extras arrays are always empty so the shape is identical across input kinds. The aliases array is populated for uv.lock findings and empty otherwise. Exit code is non-zero when any vulnerability is found.

Quick recipes with jq:

uv-audit -r pyproject.toml --all --json | jq '.vulnerable'
uv-audit -r pyproject.toml --all --json | jq '.inputs[] | select(.vulnerabilities | length > 0)'
uv-audit -r pyproject.toml --all --json | jq -r '.inputs[].vulnerabilities[].id'

Install as a uv tool

From PyPI (package name is uv-audit2, CLI command stays uv-audit):

uv tool install uv-audit2
uv-audit -r requirements.txt

Or directly from git:

uv tool install git+https://github.com/SpielerNogard/uv-audit.git@main
uv tool run uv-audit -r requirements.txt

GitHub Action

uv-audit is also packaged as a reusable GitHub Action. The action discovers pyproject.toml, requirements*.txt, and uv.lock files in your repo, scans each for known vulnerabilities, posts a sticky comment on the PR with findings, and fails the build (configurable) when non-ignored vulnerabilities are present.

Quick start (single job)

name: audit
on: [pull_request]
permissions:
  contents: read
  pull-requests: write
jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: SpielerNogard/uv-audit@0.2.0
        with:
          ignore_vulns: |
            PYSEC-2026-161

Inputs

Input Default Description
path . Root directory for discovery
include **/pyproject.toml, **/requirements*.txt, **/uv.lock Newline-separated glob patterns
exclude .venv, venv, .tox, node_modules, .git, dist, build, site-packages Path components or globs to skip
ignore_vulns (empty) Vulnerability IDs to suppress
fail_on_vuln true Exit non-zero when non-ignored vulns are found
pyproject_args --all Extra CLI args for pyproject scans
uv_audit_version matches action tag Version of uv-audit2 installed from PyPI
comment_on_pr true Create/update the sticky PR comment
github_token ${{ github.token }} Token used for the comment API

Outputs

Output Description
vulnerable 'true' or 'false'
vuln_count Number of non-ignored findings
ignored_count Number of ignored findings
report_json Aggregated JSON report

Private indexes

Composite actions inherit env: from the caller; pass any UV_* / PIP_* environment variable to authenticate against private PyPI mirrors:

- uses: SpielerNogard/uv-audit@0.2.0
  env:
    UV_INDEX_URL: https://pypi.example.com/simple
    UV_INDEX_USERNAME: ${{ secrets.PYPI_USER }}
    UV_INDEX_PASSWORD: ${{ secrets.PYPI_TOKEN }}

Metadata

Release files for uv-audit2 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for uv-audit2 0.4.1
File Size Uploaded Provenance Actions
uv_audit2-0.4.1.tar.gz 24.6 kB With provenance Details for uv_audit2-0.4.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for uv-audit2 0.4.1
File Interpreter ABI Platform Actions
uv_audit2-0.4.1-py3-none-any.whl Python 3 none any Details for uv_audit2-0.4.1-py3-none-any.whl

Total release size: 48.8 kB

Release files / uv_audit2-0.4.1.tar.gz

Download URL uv_audit2-0.4.1.tar.gz
Size 24.6 kB
Tags Source
SHA-256 checksum
How to use checksums
2c438b1d0e598dd7cc38113460e51f4e858af17e0c15f556c9e2d204507a62e6
BLAKE2b-256 checksum
How to use checksums
7d87d2f0d4d6793d29d1586826b2c213e4e9e0e4be276d9fce2a212453658535
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.

Transparency log

Release files / uv_audit2-0.4.1-py3-none-any.whl

Download URL uv_audit2-0.4.1-py3-none-any.whl
Size 24.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cc5c2eba367e87d63d31063e321116b2f108cb259aa55449919c60adcb27f7ca
BLAKE2b-256 checksum
How to use checksums
143922a5ff33020a75842753a31ab81249075339512ef794315d861ea7f93308
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.

Transparency log

Security for uv-audit2 0.4.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for uv_audit2-0.4.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page