Audit Dependencies with pip-audit and safety
Audit dependencies with pip-audit and safety — Secure development.
Focus: audit dependencies with pip-audit and safety
You've built a Python app that works perfectly in development. Then you run pip install on a fresh staging server, ship it, and months later discover a known critical vulnerability — in a transitive package you never even touched. The timer starts: patch, rebuild, redeploy, hope the exploit wasn't already used. This is exactly the pain that dependency auditing solves. In this lesson, you'll learn to audit dependencies with pip-audit and safety, so you catch those vulnerabilities before they ever reach production.
The problem this lesson solves
Every dependency you add to requirements.txt or pyproject.toml brings along its own trust obligations — and the dependencies of those dependencies. A single outdated requests version or a malicious urllib3 release can expose your data, your users, and your reputation. The Python package ecosystem is enormous and fast-moving; keeping track manually is impossible. The result is "dependency debt": silent vulnerabilities lurking in code you barely read.
The consequences are real. Supply-chain attacks are on the rise, and the average age of a known vulnerability in production systems is measured in months. The OWASP Top 10 lists "Vulnerable and Outdated Components" as a top risk — it's not just a theoretical concern. When an attacker scans your public endpoints, they often probe for known CVEs (Common Vulnerabilities and Exposures) before anything else.
The answer isn't to stop using packages — that's absurd. The answer is to make auditing a routine, automated, and fast part of your workflow. pip-audit and safety are the two most established tools for this job, and they fit naturally into your existing Python toolchain.
Core concept / mental model
Think of auditing dependencies as a security check at the border. Every package that enters your environment is a visitor; you want to verify their identity and check them against a list of known troublemakers before you let them stay.
pip-auditqueries the OSV (Open Source Vulnerabilities) database via the OSV API. It examines your environment (pip-audit --local), a requirements file, or a pip-compiled lockfile.safety(from pyup.io) uses its own curated Safety vulnerability database, which includes both CVE and non-CVE entries. It also offers a free-only tier for public projects and paid tiers with enhanced coverage.
Both tools compare the exact versions of your installed packages (or listed in your lockfile) against a collection of known vulnerable releases. If a version is not vulnerable, you're green. If it is, you get a clear report with the affected range and a fixed version.
Key terms you'll meet:
- CVE — Common Vulnerabilities and Exposures, a public identifier for a specific security flaw.
- Advisory — a formal notice about a vulnerability, usually includes affected versions and fix.
- Transitive dependency — a package that a dependency depends on; often invisible in your
requirements.txt. - Lockfile — a file that pins exact versions of every package and its dependencies (e.g.,
pip-toolsoutput, Poetry'spoetry.lock).
A practical mental model: your environment is a fleet of vehicles; pip-audit and safety are the inspection stations. You run them regularly, they inspect every vehicle, and they flag the ones with known safety recalls. You can't fix a recall you don't know about.
How it works step by step
The process is simple, but there are steps you shouldn't skip.
- Generate or maintain a full inventory — ideally a lockfile that pins exact versions of every direct and transitive package.
- Run a vulnerability scan — with
pip-auditorsafety, pointing at that inventory. - Interpret the report — for each finding, note the package, the affected versions, and the recommended fixed version.
- Update or patch — upgrade to a non-vulnerable version, or apply a workaround if no fix exists yet.
- Re-run the scan — confirm the vulnerability is gone.
- Automate — integrate the scan into your CI pipeline, so a new PR that introduces a vulnerable dependency fails the build.
- Schedule recurring scans — new advisories are published daily; run audits at least weekly or on every deploy.
For pip-audit specifically:
- It can audit your current environment (
pip-audit --local), a requirements file (pip-audit -r requirements.txt), or a lockfile (pip-audit -r requirements.txtworks if it's inrequirements.txtformat, but better to use project-specific lockfile support). - It uses a local cache of the OSV database, so it works offline after the first run.
For safety:
- It accepts a
requirements.txtfile or a full frozen environment (safety check -r requirements.txt). - The free tier is limited; you may need an API key for advanced features like ignoring CVEs with a reason.
Hands-on walkthrough
Let's get our hands dirty. First, install both tools in a virtual environment to avoid polluting your global Python.
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install pip-audit safety
Now let's create a sample project with a known vulnerable package. We'll use an older, intentionally vulnerable version of requests for demonstration.
mkdir demo_project && cd demo_project
echo "requests==2.19.1" > requirements.txt
Now run pip-audit on the requirements file:
pip-audit -r requirements.txt
Expected output (verbatim may vary):
Found 1 known vulnerability in 1 package
Name Version Fix Vulnerability ID
requests 2.19.1 2.20.0 CVE-2018-18074
Yes — requests 2.19.1 has a known vulnerability (it would set max_retries in a way that could allow a proxy to exhaust your connection pool). pip-audit tells you the fixed version.
Now run safety on the same file:
safety check -r requirements.txt
Expected output:
+===================================================================+
REPORT
+===================================================================+
Safety is using database generated...
-> Scanning dependencies in your project...
-> Found 1 vulnerabilities
+===================================================================+
Name Version Fix CVE
requests 2.19.1 2.20.0 CVE-2018-18074
Both tools flag the same issue. Now let's see a real fix in action. Update the requirement and re-scan:
echo "requests==2.22.0" > requirements.txt
pip-audit -r requirements.txt
Expected:
No known vulnerabilities found (0 packages)
Great — the vulnerability is gone.
But in reality, you'll have many more packages. Let's install a real project and audit your environment:
pip install flask==1.0.3 # old version, just for demo
pip-audit --local
You might see something like:
Found 3 known vulnerabilities in 3 packages
Name Version Fix Vulnerability ID
flask 1.0.3 1.1.0 CVE-2019-1010083
...
Now you know the drill: upgrade and re-scan.
Automating with CI
A key practice is to make these tools run automatically. Here's a minimal GitHub Actions workflow snippet:
name: Dependency Audit
on: [push, pull_request]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install pip-audit safety
- run: pip-audit -r requirements.txt
- run: safety check -r requirements.txt
This ensures every push is scanned. If an auditor finds a vulnerability, the build fails, forcing you to address it.
Compare options / when to choose what
Both tools serve the same purpose, but they have distinct strengths. Here's a comparison to help you decide:
| Feature | pip-audit | safety |
|---|---|---|
| Database | OSV (open, comprehensive) | Safety DB (curated, includes non-CVEs) |
| Cost | free, open source | free tier (limited), paid for full features |
| Offline support | yes (local cache) | no (always queries API) |
| Output formats | JSON, cyclonedx, etc. | text, JSON |
| Best for | CI pipelines, open-source projects | Enterprises needing curated data, compliance |
| Integration | pip-audit, pip-audit --local |
safety check, safety review |
When to choose pip-audit: you're in a CI environment, you want a modern tool with a clean API, and you don't need the extra curated database. It's the default recommendation for new projects.
When to choose safety: you need legal/compliance reporting, want to ignore specific CVEs with a documented reason, or you're in a corporate environment that values the Safety database's extra entries. The paid tiers offer vulnerability analytics and a policy library.
Pragmatic approach: use both. Run pip-audit for a quick, open-source baseline, and run safety with a paid API key if you need advanced features. They're fast and cheap to run in CI.
Troubleshooting & edge cases
Even with powerful tools, you'll run into issues. Here are the most common ones and how to handle them.
1. False positives / false negatives
- False positive: A package is flagged but you're not using the affected module, or you're on a non-vulnerable code path. Investigate the advisory before panicking.
pip-auditincludes a--fixflag that attempts to upgrade, but you should review the advisory to confirm the reachability. - False negative: No vulnerability found, but you suspect something. This happens when the database hasn't caught up with a new advisory. Update your local cache (
pip-audit --refresh) and consider using multiple databases (bothpip-auditandsafety).
2. Vulnerabilities with no fix yet
Sometimes the advisory is real, but the package maintainer hasn't released a patched version. In that case:
- Check for workarounds — often the vulnerability is only reachable under certain conditions; the advisory may suggest mitigations like "disable X" or "restrict network access".
- Pin the vulnerable version with a comment explaining why you're accepting the risk, and set a reminder to revisit. Tools like
safetylet you ignore a specific CVE with a reason via the--ignoreoption, which is useful for documenting your decision.
3. Git dependencies or local packages
Neither tool can audit packages installed from Git URLs or local paths, because they don't have a version number that maps to the database. For these, you'll need to manually track or rely on the external source for advisories.
4. Large lockfiles slow to scan
The scan can be slow on huge projects. Use pip-audit's --local mode if you've already installed, or restrict to your runtime dependencies (exclude dev tools) to speed up CI.
5. Pipeline failing due to a known vuln in a dev-only package
If the vulnerability is only in a development dependency (e.g., pytest), you can separate dev requirements from production requirements, and only audit the production list in your critical path.
6. API rate limits (safety free tier)
safety's free tier has a limited number of queries per day. If you hit the limit, wait or upgrade. Alternatively, use pip-audit as your main scanner and reserve safety for pre-release checks.
7. Virtual environments and global installs
If you're auditing a project, always audit within its virtual environment (pip-audit --local). Auditing your global Python will produce a lot of noise and might fail due to system packages.
What you learned & what's next
You've now added a critical tool to your security belt: you can audit dependencies with pip-audit and safety to uncover known vulnerabilities before they become exploits. You learned how to generate a vulnerability inventory, run scans, interpret reports, fix issues, and automate the process in CI. You also learned how to compare the two tools and handle edge cases like false positives and unfixed vulnerabilities.
This directly reinforces your secure-development skills — auditing is part of a proactive security posture. The next lesson in this track will take you deeper into threat modeling, teaching you how to think like an attacker and systematically evaluate the attack surface of your application. With dependency auditing now automated, you'll have one less blind spot on your security journey.
Practice recap
Try auditing your own project right now: install pip-audit and safety, generate a lockfile with pip freeze > requirements.txt (or use pip-tools), then run both scanners. Fix at least one real vulnerability you find, and re-scan to confirm. Next, add the CI workflow from this lesson to one of your GitHub repositories.
Common mistakes
- Only auditing direct dependencies — transitive dependencies are the majority of real-world vulnerabilities. Use
pip-audit --localor a lockfile to capture the full dependency tree. - Ignoring negative output — assuming 'no vulnerabilities' means your code is secure. Auditing only checks known CVEs; you still need to test for logic flaws and coverage of your actual use of the package.
- Running the auditor on a global Python environment — it will flag system packages and won't reflect your project's actual pinned versions. Always use a virtual environment and audit with
--localor-r requirements.txt. - Skipping the re-scan after fixing a version — it's easy to forget, but only a second scan confirms the vulnerability is truly gone.
Variations
- Use
pip-auditwith the--fixflag to automatically attempt upgrading vulnerable packages to the first non-vulnerable version (when available). - Use
safetywith--full-reportto generate a detailed, human-readable report with CVE descriptions and recommended actions. - For an audit-as-code approach, integrate
pip-auditinto a pre-commit hook or a GitHub Action that fails the build on any new vulnerability.
Real-world use cases
- A Django web app undergoing a quarterly security review — run
pip-audit -r requirements.txtin CI to catch new advisories against locked versions. - A data science pipeline with a large, often-ignored dependency tree — schedule a weekly
pip-audit --localto detect vulnerabilities in packages likepandasornumpythat only surface in production. - A startup preparing for a SOC 2 audit — use
safetywith a paid plan to produce compliance-grade reports documenting ignored CVEs with justification.
Key takeaways
- Auditing dependencies with
pip-auditandsafetyis a fast, reliable way to discover known vulnerabilities in your Python project before attackers do. - Always audit the full dependency tree, including transitive dependencies, using a lockfile or
--localin a virtual environment. - Fix vulnerabilities by upgrading to the recommended patched version, then re-run the audit to confirm.
- Automate the audit in CI with a simple workflow to keep new vulnerabilities from entering your codebase.
- Choose
pip-auditfor open-source, CI-friendly scanning; choosesafetyfor curated data and compliance features. - Handle edge cases like false positives and unfixed advisories with investigation and documented risk acceptance.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.