Scan Python Packages for Known CVEs with pip-audit
How to use pip-audit to scan Python dependencies for known vulnerabilities, configure ignore lists, integrate with CI/CD, and remediate findings.
Note: This guide follows English-language naming conventions and terminology standards common in international development teams. Examples use English identifiers and comments to maximize compatibility across codebases and tooling.
Overview
pip-audit is a tool for scanning Python package dependencies for known security vulnerabilities. It queries the PyPI Advisory Database (OSV) and reports CVEs affecting your installed packages. Unlike safety (which requires a paid API key for full coverage), pip-audit is free, open-source, and uses the OSV.dev database maintained by Google.
When to Use
- Scanning Python projects for vulnerable dependencies
- CI/CD pipelines to catch vulnerable packages before deployment
- Regular security audits of production environments
- Checking if a package upgrade introduces new vulnerabilities
- Validating that a vulnerability has been patched after upgrading
When NOT to Use
- Scanning source code for insecure patterns — use Bandit instead
- Non-Python projects — pip-audit only checks Python packages
- When you need SCA (Software Composition Analysis) for licenses — use
pip-licenses - For runtime vulnerability detection — pip-audit is static, checking installed versions
Solution
Install pip-audit
pip install pip-audit
# Using poetry
poetry add --group dev pip-audit
# Using pipenv
pipenv install --dev pip-audit
# Using uv
uv add --dev pip-audit
Basic scan
# Scan the current environment
pip-audit
# Scan a requirements.txt file
pip-audit -r requirements.txt
# Scan multiple requirements files
pip-audit -r requirements.txt -r requirements-dev.txt
# Output in JSON format
pip-audit -f json -o audit-report.json
# Output in CSV format
pip-audit -f csv -o audit-report.csv
# Output in Markdown
pip-audit -f markdown -o audit-report.md
Scan with project manifests
# Scan pyproject.toml (poetry/setuptools/hatch)
pip-audit --requirement pyproject.toml
# Scan Pipfile.lock
pip-audit --requirement Pipfile.lock
# Scan poetry.lock
pip-audit --requirement poetry.lock
# Scan uv.lock
pip-audit --requirement uv.lock
Strict mode — fail on any vulnerability
# Exit with non-zero code if any vulnerabilities found
pip-audit --strict
# Use in CI/CD to fail the build
pip-audit --strict -f json -o audit-report.json
Ignore specific vulnerabilities
# Ignore by CVE ID
pip-audit --ignore-vuln CVE-2023-12345
# Ignore multiple CVEs
pip-audit --ignore-vuln CVE-2023-12345 --ignore-vuln CVE-2023-67890
# Ignore by GHSA (GitHub Security Advisory) ID
pip-audit --ignore-vuln GHSA-xxxx-xxxx-xxxx
Configuration file
# pyproject.toml
[tool.pip-audit]
strict = true
requirement = ["requirements.txt"]
ignore-vuln = ["CVE-2023-12345", "GHSA-xxxx-xxxx-xxxx"]
output-format = "json"
output-file = "audit-report.json"
Fix vulnerabilities automatically
# Automatically upgrade vulnerable packages
pip-audit --fix
# Dry run — show what would be upgraded
pip-audit --fix --dry-run
# Fix and write to requirements.txt
pip-audit --fix --require-hashes
Scan with hash checking
# Verify package hashes during scan
pip-audit --require-hashes -r requirements.txt
# This ensures packages haven't been tampered with
CI/CD integration with GitHub Actions
# .github/workflows/pip-audit.yml
name: pip-audit
on: [push, pull_request, schedule]
# Schedule: run weekly to catch new CVEs
on:
push:
pull_request:
schedule:
- cron: '0 6 * * 1' # Every Monday 6 AM UTC
jobs:
pip-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install pip-audit
run: pip install pip-audit
- name: Run pip-audit
run: |
pip-audit -r requirements.txt -f json -o audit-report.json || true
- name: Check for HIGH severity vulnerabilities
run: |
HIGH_COUNT=$(python -c "
import json
with open('audit-report.json') as f:
data = json.load(f)
# Count dependencies with vulnerabilities
count = sum(1 for dep in data.get('dependencies', []) if dep.get('vulns'))
print(count)
")
if [ "$HIGH_COUNT" -gt 0 ]; then
echo "Found $HIGH_COUNT packages with vulnerabilities"
cat audit-report.json
exit 1
fi
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: pip-audit-report
path: audit-report.json
CI/CD with Poetry
# .github/workflows/pip-audit-poetry.yml
name: pip-audit (Poetry)
on: [push, pull_request]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install Poetry
run: pip install poetry
- name: Install dependencies
run: poetry install --no-root
- name: Run pip-audit
run: |
poetry run pip-audit --requirement poetry.lock --strict -f json -o audit-report.json
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: pip-audit-report
path: audit-report.json
Pre-commit hook
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pypa/pip-audit
rev: v2.7.3
hooks:
- id: pip-audit
args: ["--strict", "--requirement", "requirements.txt"]
files: ^requirements\.txt$
Makefile integration
# Makefile
.PHONY: audit
audit:
pip-audit -r requirements.txt --strict -f json -o audit-report.json
@echo "Audit complete. Report: audit-report.json"
.PHONY: audit-fix
audit-fix:
pip-audit -r requirements.txt --fix --dry-run
.PHONY: audit-html
audit-html:
pip-audit -r requirements.txt -f html -o audit-report.html
Remediation workflow
# 1. Identify vulnerable packages
pip-audit -r requirements.txt
# Output:
# Name Version Fix Versions CVE ID Description
# requests 2.28.0 2.31.0 CVE-2023-32681 Proxy-Authorization header leak
# 2. Check what changed in the fix
pip install requests==2.31.0 --dry-run
# 3. Upgrade the vulnerable package
pip install --upgrade requests==2.31.0
# 4. Update requirements.txt
pip freeze | grep requests > requirements.txt
# 5. Re-scan to verify fix
pip-audit -r requirements.txt
Using pip-audit with constraints
# Scan with constraints (e.g., Python version constraints)
pip-audit -r requirements.txt -c constraints.txt
# constraints.txt
# requests>=2.31.0
# urllib3>=2.0.0
Variants
pip-audit with Docker
# Dockerfile — scan during build
FROM python:3.12-slim AS audit
RUN pip install pip-audit
COPY requirements.txt .
RUN pip-audit -r requirements.txt --strict
FROM python:3.12-slim
COPY --from=audit /app /app
WORKDIR /app
RUN pip install -r requirements.txt
CMD ["python", "main.py"]
pip-audit with uv
# Install pip-audit with uv
uv add --dev pip-audit
# Scan uv.lock
uv run pip-audit --requirement uv.lock --strict
# Scan in CI
uv run pip-audit --requirement uv.lock -f json -o audit-report.json
pip-audit with Dependabot
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
# Dependabot opens PRs for outdated packages
# pip-audit catches CVEs that Dependabot might miss
Scan virtual environments
# Scan the current virtual environment
source .venv/bin/activate
pip-audit
# Scan a specific environment
pip-audit --path /path/to/venv
# Scan the system Python
pip-audit --system
Best Practices
-
For a deeper guide, see Find Security Issues in Python Code with Bandit.
-
Run pip-audit in CI/CD on every push — catch vulnerable packages early
-
Schedule weekly scans — new CVEs are published daily
-
Use
--strictin CI — fail the build on any vulnerability -
Document ignored CVEs — create an ignore policy with expiration dates
-
Combine with Bandit — pip-audit checks dependencies, Bandit checks code
-
Pin package versions — use
requirements.txtwith exact versions, not ranges -
Review audit reports regularly — don’t just ignore and forget
-
Use
--fixcautiously — upgrading packages can introduce breaking changes
Common Mistakes
- Ignoring all vulnerabilities: using
--ignore-vulnfor every finding without investigating. Some CVEs are critical and need immediate attention. - Not pinning versions: scanning unpinned requirements (
requests>=2.0) gives inaccurate results. Pin exact versions. - Only scanning production requirements: dev dependencies can also have vulnerabilities. Scan both.
- Not scheduling regular scans: running pip-audit once is not enough. New CVEs are published daily.
- Upgrading without testing:
--fixupgrades packages automatically, which can break your code. Always test after upgrading.
FAQ
What is pip-audit?
A tool that scans Python package dependencies for known security vulnerabilities using the OSV.dev database. It’s free, open-source, and maintained by the Python Packaging Authority (PyPA).
How does pip-audit differ from safety?
pip-audit is free and uses the OSV.dev database. safety requires a paid API key for full vulnerability coverage. Both scan Python dependencies, but pip-audit is the recommended free option.
What database does pip-audit use?
pip-audit uses the OSV.dev database maintained by Google, which aggregates vulnerabilities from PyPA Advisory Database, GitHub Security Advisories, and NVD.
Should I use pip-audit or Dependabot?
Both. Dependabot opens PRs to upgrade outdated packages. pip-audit scans for known CVEs and can catch issues Dependabot misses. Use them together.
Can pip-audit fix vulnerabilities automatically?
Yes, use pip-audit --fix to automatically upgrade vulnerable packages. Use --dry-run first to see what would change. Always test after upgrading.
Related Resources
Find Security Issues in Python Code with Bandit
How to use Bandit to scan Python code for common security vulnerabilities, configure ignore lists, integrate with CI/CD, and interpret results.
RecipeStrict Type Checking in Python with mypy
How to configure mypy strict mode for Python projects, handle common type errors, use Protocol and TypeGuard, and integrate with CI/CD.
RecipeShare Workflow Logic with GitHub Actions Reusable Workflows
How to create and consume reusable workflows in GitHub Actions, covering inputs, secrets, conditional jobs, matrix strategy, and organization-wide sharing.