mrkeyoor.com_
Sat 08 Aug 21:01 UTC
PyPIUtilsupdated 08 Aug 2026

pip-audit

A command line scanner that takes the Python packages you have installed, or the ones a requirements file resolves to, and checks each name and version against a vulnerability feed. The default feed is the PyPI JSON API backed by the Python Packaging Advisory Database; OSV is available with a flag. Output comes as a table, Markdown, JSON, or a CycloneDX SBOM, and --fix will upgrade the affected packages in place. It lives under the pypa organisation and is maintained in part by Trail of Bits with support from Google.

Verdict

The default choice for checking Python dependencies against published advisories: pypa-maintained, no account needed, and it drops into CI in one line. Go in knowing it only compares names and versions against a feed, so a clean run means no advisory matched, not that your dependencies are safe.

API stability4/5The flags that matter (-r, -l, -f, -s, --fix, --ignore-vuln) have been stable across the 2.x line, and the JSON output shape is what people build on. Newer options such as --locked and the esms service were added rather than replacing anything. There is no supported Python API: the package documents a command line tool, so importing pip_audit internals is on you.
Docs4/5The README is the documentation and it is unusually candid: a full generated help dump, an explicit exit code table, a troubleshooting section covering slow runs and private indexes, and a security model section that states plainly what the tool cannot protect you from. What is missing is anything beyond that single page, so there is no reference for the JSON schema or for extending it.
Maintenance4/5Version 2.10.1 was released on 2026-06-10 and the repository was pushed on 2026-08-07, so work is ongoing. Feature releases are spaced out (2.9.0 in April 2025, 2.10.0 in December 2025) and 69 issues and pull requests are open, which reads as a small team keeping a stable tool current rather than a fast-moving project.
Ecosystem4/5Roughly 6,600,715 weekly downloads and 1,345 stars, with an official GitHub Action (pypa/gh-action-pip-audit), a pre-commit hook, and conda-forge packaging. Living under the pypa organisation and reading the PyPI advisory feed means it stays aligned with how Python packaging actually distributes advisories.

Use it if

  • You want a CI gate that fails the build when a dependency has a published advisory, since the exit code is 1 whenever anything is found and cannot be suppressed from inside the tool
  • You need a CycloneDX SBOM out of the same run that does the audit, in JSON or XML, without adding a second tool
  • You already pin and hash your requirements, because --require-hashes and --no-deps then skip resolution entirely and the audit finishes in seconds
  • You prefer the advisory data that PyPI itself serves, rather than a vendor database that needs an account or an API token
Skip it if

Setup reality

pip install pip-audit needs Python 3.10 or newer and pulls a fairly wide tree of its own (rich, requests, CacheControl, cyclonedx-python-lib, pip-api, pip-requirements-parser), so install it in its own environment or with a tool runner rather than next to the code you are auditing. Decide early which mode you are in, because the modes behave very differently: bare pip-audit audits the environment you are standing in, pip-audit -l narrows that to non-system packages, pip-audit -r requirements.txt resolves the file in a temporary virtualenv, and pip-audit . reads a local project. Resolution is the slow part and the part that runs arbitrary package code, so pinned plus hashed inputs with --require-hashes are both faster and safer. The exit code is deliberately not configurable: 0 for clean, 1 for anything found, and the documented workaround for a soft check is a shell || true. There is no ignore file, so every accepted finding becomes another --ignore-vuln flag in your CI config that nobody remembers to remove. Private indexes work through --index-url and --extra-index-url but interactive authentication does not, keyring only works through the subprocess provider, and some registries need a hardcoded username. Expect the first run against a mature project to produce findings you cannot act on, because the feeds carry advisories with no fix version; filtering those out means post-processing the JSON output yourself.

Patterns

Scan the environment you are inaudit-current-environment

pip-audit

# only packages installed in this virtualenv, not system site-packages
pip-audit --local

Bare pip-audit audits whatever interpreter is on PATH. Inside a container that often includes distro-provided packages, which is what --local filters out.

Audit a requirements fileaudit-requirements-file

pip-audit -r requirements.txt

# multiple files in one run
pip-audit -r requirements.txt -r requirements-dev.txt

This resolves the file in a temporary environment, which executes package build code. Treat it with the same trust you would give pip install -r on the same file.

Make a pinned audit fastskip-dependency-resolution

# fails if anything is not pinned to an exact version
pip-audit --no-deps -r requirements.txt

# stricter: also requires --hash entries
pip-audit --require-hashes -r requirements.txt

Both skip resolution, which is where nearly all the runtime goes. --require-hashes is the better default because it also checks integrity.

Gate a CI job on the exit codefail-ci-build

- name: Audit dependencies
  run: pip-audit -r requirements.txt --require-hashes

# or use the official action
- uses: pypa/gh-action-pip-audit@v1.1.0
  with:
    inputs: requirements.txt

Exit code 1 means findings, 0 means none, and there is no flag to soften it. For a warn-only job append || true and read the output instead.

Suppress a specific advisoryignore-known-finding

pip-audit --ignore-vuln GHSA-w596-4wvx-j9j6

# repeat the flag for each one
pip-audit --ignore-vuln PYSEC-2023-100 --ignore-vuln CVE-2024-12345

Aliases work, so a GHSA or CVE id is accepted where the feed only has a PYSEC id. There is no expiry and no place to record why, so review the list on a schedule.

Get JSON for downstream processingemit-machine-readable

pip-audit -r requirements.txt -f json -o audit.json

# only fail when a fix actually exists
test -z "$(pip-audit -r requirements.txt -f json 2>/dev/null \
  | jq '.dependencies[].vulns[].fix_versions[]')"

Descriptions and aliases default to on for the json format, so the file is large. The jq form is the documented way to ignore findings with no released fix.

Produce a CycloneDX SBOMgenerate-sbom

pip-audit -r requirements.txt -f cyclonedx-json -o sbom.json
pip-audit -r requirements.txt -f cyclonedx-xml -o sbom.xml

The --desc and --aliases flags have no effect on either CycloneDX format. The exit code still reflects findings, so an SBOM run can fail your build.

Audit against OSV instead of PyPIswitch-vulnerability-service

pip-audit -s osv -r requirements.txt

# self-hosted or mirrored OSV endpoint
pip-audit -s osv --osv-url https://osv.internal.example/v1/query

The two feeds do not report identically. Running both and comparing is a reasonable one-off exercise before you commit to one in CI.

Audit a project or its lock filesaudit-project-directory

pip-audit .

# read pylock.*.toml instead of resolving pyproject.toml
pip-audit --locked .

Only pyproject.toml and pylock.*.toml are recognised. Poetry, pipenv and uv projects need an export to requirements format first.

Upgrade affected packagesauto-upgrade-vulnerable

# see what would change without touching anything
pip-audit --fix --dry-run

pip-audit --fix

--fix upgrades to the first fixed version, ignoring whether that crosses a major boundary. Always run the dry run first and re-run your test suite after.

Wire it into pre-commitrun-via-pre-commit

- repo: https://github.com/pypa/pip-audit
  rev: v2.10.1
  hooks:
    - id: pip-audit
      args: ["-r", "requirements.txt"]

ci:
  skip: [pip-audit]

The ci.skip entry is needed because pre-commit.ci blocks network calls and the hook cannot reach the advisory feed there.

Point at an internal package indexaudit-private-index

pip-audit \
  --index-url https://pypi.internal.example/simple \
  --extra-index-url https://pypi.org/simple \
  -r requirements.txt

There is no interactive prompt for credentials. Keyring works only through the subprocess provider, and registries such as Google Artifact Registry need their fixed username supplied.

Alternatives

PackageRegistryPick it when
safetyPyPIYou want a commercial vulnerability database with policy files, expiring ignores and a hosted dashboard behind it
banditPyPIYou need static analysis of your own Python code for insecure patterns, which is the gap pip-audit deliberately does not cover
cyclonedx-bomPyPISBOM generation is the actual goal and vulnerability matching happens later in a separate scanner