Skip to content
SP StackPractices
beginner By Mathias Paulenko

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.

Topics: security

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 --strict in 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.txt with exact versions, not ranges

  • Review audit reports regularly — don’t just ignore and forget

  • Use --fix cautiously — upgrading packages can introduce breaking changes

Common Mistakes

  • Ignoring all vulnerabilities: using --ignore-vuln for 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: --fix upgrades 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.