S
Home How-Tos Updated 2026-07-20

Scan Python Dependencies for Vulnerabilities with pip-audit

Scan Python Dependencies for Vulnerabilities with pip-audit

pip-audit is the PyPA (Python Packaging Authority) project's own tool for checking installed or pinned Python dependencies against known vulnerability databases. It's the most direct way to answer "does anything in this project have a known CVE" without standing up a full SCA platform — and it's a natural complement to the dependency-scanning practices covered in SAST and SCA.


What It Checks

pip-audit reads either your currently installed environment or a requirements.txt-style file, resolves the package/version list, and checks each one against a vulnerability data source — by default the PyPI Advisory Database (--vulnerability-service pypi), or optionally OSV (--vulnerability-service osv), Google's open, cross-ecosystem vulnerability database. It reports known CVEs/advisories per package, and can optionally attempt to auto-upgrade affected packages to a fixed version.


Installation

Install it as an isolated tool with pipx (recommended — keeps it out of your project's own dependency tree) rather than into every project virtual environment:

pipx install pip-audit

Or into a specific virtual environment if you want it available inside that env directly:

python -m venv .venv
source .venv/bin/activate   # .venv\Scripts\activate on Windows
pip install pip-audit

Verify:

pip-audit --version

Basic Usage — Auditing an Environment

Run with no arguments to audit every package installed in the current Python environment (the one pip-audit itself is running under, or the one you point it at):

pip-audit

Sample output:

Found 2 known vulnerabilities in 2 packages
Name    Version Vuln ID           Fix Versions
------- ------- ----------------- ------------
requests 2.25.1 GHSA-j8r2-6x86-q33q 2.31.0
pyyaml   5.3.1  PYSEC-2021-142      5.4

To audit a different environment without activating it (useful for auditing another project's venv from a CI runner):

pip-audit --path /path/to/other/.venv

Auditing a requirements.txt / Lock File Directly

For CI pipelines, it's usually more useful to audit the pinned dependency file itself, rather than whatever happens to be installed:

pip-audit -r requirements.txt

This resolves the file's contents (using pip under the hood) rather than requiring the packages to already be installed — appropriate for a pre-install gate in a pipeline, before you've actually built the environment.


Output Formats and SBOM Generation

Default output is a human-readable column table. For machine consumption or software bill-of-materials use cases:

# JSON, for parsing in a pipeline
pip-audit -r requirements.txt -f json -o audit-results.json

# CycloneDX SBOM (JSON or XML), consumable by SBOM tooling / supply-chain platforms
pip-audit -r requirements.txt -f cyclonedx-json -o sbom.json
pip-audit -r requirements.txt -f cyclonedx-xml -o sbom.xml

# Markdown, for pasting into a PR comment or report
pip-audit -r requirements.txt -f markdown

--desc includes the full vulnerability description in the output (useful for reports; noisier for quick console checks):

pip-audit -r requirements.txt --desc on

Auto-Fixing Vulnerable Dependencies

pip-audit can attempt to upgrade affected packages to the earliest version that resolves the reported vulnerability:

# Preview what would change, without modifying anything
pip-audit -r requirements.txt --fix --dry-run

# Apply the upgrades
pip-audit -r requirements.txt --fix

Treat --fix as a suggestion to review, not something to run unattended against a requirements.txt you then commit blindly — an automatic minor/major version bump can still introduce breaking changes independent of the security fix itself. Run your test suite before committing the result.


Suppressing a Specific Finding (Accepted Risk)

Sometimes a fix isn't immediately available, or the vulnerable code path isn't reachable in how you use the package. Suppress a specific advisory explicitly rather than ignoring the tool's exit code entirely — this keeps the suppression visible and scoped to exactly one finding:

pip-audit -r requirements.txt --ignore-vuln GHSA-j8r2-6x86-q33q

--ignore-vuln is repeatable for multiple IDs. Track why each one is suppressed somewhere reviewable (a comment in your CI config, a ticket reference) — an --ignore-vuln flag with no accompanying justification just becomes an unreviewed permanent exception, the same failure mode as an unreviewed firewall rule or access grant.


Choosing a Vulnerability Data Source

# Default — PyPI's own advisory database
pip-audit -r requirements.txt --vulnerability-service pypi

# OSV — broader, cross-ecosystem, community-fed database
pip-audit -r requirements.txt --vulnerability-service osv

The two sources don't always carry identical coverage or identifiers for the same underlying issue — running both periodically (e.g., PyPI in a fast pre-merge check, OSV in a slower scheduled scan) catches more than relying on either alone.


CI/CD Integration

Generic CI Step (any platform)

The most portable approach works identically in GitHub Actions, GitLab CI, Jenkins, or any runner with Python available — install and run it as a step, and gate the pipeline on the exit code (pip-audit exits non-zero when vulnerabilities are found):

pip install pip-audit
pip-audit -r requirements.txt --strict

--strict makes resolution failures or vulnerability-service errors fail the run loudly instead of silently skipping affected packages — appropriate for a gating check where a silent skip would defeat the purpose.

GitHub Actions Example

name: Dependency Audit
on: [push, pull_request]

jobs:
  pip-audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - name: Install and run pip-audit
        run: |
          pip install pip-audit
          pip-audit -r requirements.txt --strict --progress-spinner off

--progress-spinner off keeps CI logs clean by disabling the interactive spinner, which otherwise produces noisy escape-sequence output in a non-TTY log.

The pip-audit maintainers also publish an official GitHub Action (pypa/gh-action-pip-audit) if you'd rather use a marketplace action than a manual install step — pin it to a specific release tag from its repository rather than a floating branch.

Local Pre-Commit Hook

# .pre-commit-config.yaml
- repo: local
  hooks:
    - id: pip-audit
      name: pip-audit
      entry: pip-audit -r requirements.txt --strict
      language: system
      pass_filenames: false

A local (non-mirrored) hook is deliberate here — it calls whatever pip-audit is already on PATH rather than pinning to a specific pre-commit-hosted hook repo, keeping the check identical to what CI runs.


Handling Private Package Indexes

If your project resolves against an internal/private index rather than public PyPI, point pip-audit at it the same way you would pip — it shells out to pip for resolution when auditing a requirements file, so standard pip index configuration (--index-url, environment variables, or pip.conf) applies:

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

Troubleshooting

Symptom Likely Cause Fix
pip-audit reports nothing found, but you expected a hit Auditing the wrong environment/file Confirm you're targeting the right target with --path or -r; check you're not shadowing packages in a different venv
Resolution errors when auditing a requirements file A dependency can't be resolved without extra index config, or requires build tooling pip-audit can't satisfy Confirm plain pip install -r requirements.txt succeeds first — pip-audit inherits the same resolution behavior
CI passes locally but fails in the pipeline (or vice versa) Different vulnerability database snapshot at time of run, or --strict masking a skip locally Run with --strict in both places; treat a fresh CVE appearing as a real finding, not a flaky check
--fix changes more than expected A dependency has intermediate breaking releases between your pinned version and the fixed version Use --dry-run first, and review/test the diff before committing
Suppressed finding reappears after upgrade --ignore-vuln targets a specific ID that no longer matches after a version bump introduced a different advisory Re-run without the ignore list periodically to confirm suppressions are still the right ones

References

The Security Architecture Site — for internal reference use. Back to How-Tos