Safeguard
Security Guides

Auditing Python Dependencies with pip-audit: A Practical Guide

pip-audit is the PyPA-backed tool for scanning Python dependencies against the OSV and PyPI advisory databases. Here is how to run it well — and where it needs backup.

Priya Mehta
Security Researcher
5 min read

Python's dependency story is deceptively calm. You pip install a web framework, it pulls in a serializer, which pulls in a parser, which pulls in a C-extension wrapper — and any one of those can carry a known vulnerability that never appears in your requirements.txt. Because Python resolves dependencies at install time and pins are optional, two developers on the same team can end up with materially different transitive graphs. Auditing that graph is not optional once you are shipping anything real, and the tool built for the job is pip-audit.

Why pip-audit is the right starting point

pip-audit is maintained under the Python Packaging Authority umbrella. It queries the OSV database and the PyPI Advisory Database, which means it draws on the same curated advisory data that most serious Python tooling relies on. Unlike ad-hoc scripts that only read your requirements file, pip-audit can audit the actual resolved environment, catching transitive packages that no manifest mentions.

Install it into your project's virtual environment and run it with no arguments to audit everything currently installed:

pip install pip-audit
pip-audit

That resolves the live environment and reports each vulnerable package, the affected version, the fixed version, and the advisory ID. To audit a specific dependency file instead of the installed environment — the right choice in CI, where you want to test the declared set — point it at your requirements:

pip-audit -r requirements.txt

pip-audit understands modern packaging too. It can resolve and audit a project defined by pyproject.toml, and it works with locked files produced by tools like pip-tools or uv. For richer triage, ask it to include the vulnerability descriptions:

pip-audit -r requirements.txt --desc

Fixing and integrating

pip-audit can attempt upgrades itself. The --fix flag rewrites your requirements to the nearest non-vulnerable version:

pip-audit -r requirements.txt --fix

For pipelines, emit JSON (or one of the SBOM formats it supports) so another system can consume the results:

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

A pragmatic CI setup runs pip-audit against your locked requirements on every pull request and fails the job on any finding, while allowing a curated ignore list for issues you have formally triaged:

pip-audit -r requirements.txt --ignore-vuln GHSA-xxxx-xxxx-xxxx

A note on environments and containers

Two subtleties trip teams up. First, pip-audit can only audit packages it can identify by name and version on an index — dependencies installed from a Git URL, a local path, or an editable checkout may not resolve to an advisory match, so those slip through a naive audit. Pin such dependencies to published, versioned releases wherever you can. Second, the environment you audit must be the environment you ship. Auditing a developer's machine while your container image was built from a different resolution is a common and dangerous mismatch — run pip-audit inside your built image, or against the exact locked file the image is built from, so the report describes production rather than someone's laptop.

The limits you will hit

pip-audit is well-built and well-maintained, but a scanner that maps installed versions to advisories can only take you so far.

  • No reachability. pip-audit reports that a vulnerable version is present. It cannot tell you whether your code imports the affected module, let alone calls the vulnerable function. A deserialization flaw in a library you only use for its logging helper is reported identically to one in your core request path.
  • Environment drift. Auditing the installed environment gives accurate results, but only for that environment. If your CI resolves different versions than production — common when pins are loose — the audit and reality diverge.
  • Advisory latency. Freshly published malicious packages, typosquats, and dependency-confusion attacks do not have advisories yet. pip-audit will not catch a package that was compromised this morning.
  • One-shot, no policy. Each run is a snapshot with no notion of SLAs, ownership, accepted-risk expiry, or a shared suppression history your auditors can inspect.

Layering continuous, prioritized analysis

Keep pip-audit as your fast local and CI gate — it is the right tool for that job. Then add a continuous layer that turns a flat list of findings into a ranked worklist. Safeguard's software composition analysis audits the same resolved Python graph but adds import- and call-path reachability, so the findings that sit in code you actually execute rise to the top and the rest are visibly deprioritized rather than clogging the queue.

When you want an explanation you can act on, the Griffin AI analysis engine describes exactly how a given CVE is reachable in your codebase, what an exploit path would look like, and whether the safe upgrade is likely to break anything — the reasoning a human triager would otherwise do by hand. And when the upgrade is safe, the autonomous auto-fix workflow raises the pull request for you, tests included.

For teams costing this out across many repositories, the pricing overview shows how continuous scanning scales beyond the single-repo, run-it-manually model that pip-audit alone implies.

Checklist for a healthy Python audit process

  1. Pin dependencies in a lockfile so audits are reproducible across machines and CI.
  2. Run pip-audit -r <locked-requirements> on every pull request and fail on new findings.
  3. Use --fix for straightforward patches and review the resulting diff.
  4. Maintain an explicit, reviewed ignore list rather than muting findings ad hoc.
  5. Audit the installed environment in staging too, to catch drift between declared and resolved versions.
  6. Add continuous, reachability-aware scanning so engineers spend their time on exploitable issues, not on the long tail of unreachable ones.

pip-audit answers a precise and valuable question: which of my installed Python packages have known advisories? Pair it with reachability and autonomous remediation and you move from knowing about vulnerabilities to closing the ones that matter.

Ready to prioritize your Python findings by real exploitability? Get started free or explore the documentation.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.