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 |