PRACTITIONER GUIDE
Practitioner Guide12 min read

Python Dependency Security: Auditing PyPI Packages, pip-audit, and Supply Chain Hardening

80-150
transitive packages in a typical Python app with 10 direct dependencies, each a potential CVE source
--require-hashes
pip flag that verifies SHA-256 of every downloaded package, blocking tampered or substituted packages
30 min
estimated time to add pip-audit to a CI pipeline and surface your first dependency CVE findings
> 500
malicious PyPI packages removed in 2023 alone, most using typosquatted names of popular libraries

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

Python's dependency ecosystem has unique supply chain characteristics that make it more challenging to secure than some alternatives. PyPI has minimal package publishing controls — anyone can publish a package with any name that is not already taken. Typosquatted package names, dependency confusion attacks, and packages that execute malicious code at install time have all been documented at scale. Meanwhile, legitimate popular packages regularly publish CVE fixes, and applications with hundreds of transitive dependencies accumulate vulnerability debt silently.

The tools to manage this — pip-audit, OSV-Scanner, and lockfiles with hash pinning — are mature and free. The gap between their existence and most teams using them is typically awareness and CI/CD integration effort. This guide provides both.

Building a Python dependency security baseline

Before enforcing any pipeline gates, you need a clear picture of your current vulnerability landscape across all Python services. This section covers how to run pip-audit and OSV-Scanner to generate a baseline findings report, interpret the output by severity and exploitability, and prioritize remediation starting with critical CVEs. It also addresses the most common baseline problem in Python environments: the absence of a lockfile, which makes the dependency set non-reproducible and unauditable. Running these scans for the first time in a mature codebase typically surfaces 5-20 vulnerabilities in transitive packages that the team was unaware of.

Scan the current environment and generate a baseline

Run pip-audit -r requirements.txt -f json -o baseline.json on every Python service in your organization. Review the output: total packages scanned, total vulnerabilities found, and the breakdown by severity (critical, high, medium, low). In most mature codebases, a first scan reveals 5-20 vulnerabilities in transitive dependencies. Create a remediation priority list: fix critical CVEs first (CVSS 9.0+), then high (7.0-8.9), then address the lockfile absence if requirements.txt does not include hashes. The baseline scan establishes where you are before enforcing CI gates — trying to enforce pip-audit --strict before knowing your baseline creates a pipeline that fails on day one.

Add lockfile generation to all services without one

If your Python service uses only requirements.txt with version ranges (requests>=2.28.0) rather than pinned versions with hashes, generate a lockfile immediately. Using pip-tools: pip install pip-tools; pip-compile requirements.in --generate-hashes -o requirements.txt. The output includes exact pinned versions and SHA-256 hashes for all direct and transitive dependencies. Commit this file to version control. Update only when explicitly running pip-compile --upgrade. The lockfile prevents the 'it worked in dev but broke in prod because a different version was installed' problem and makes pip-audit output reproducible across environments.

Supply chain hardening beyond vulnerability scanning

Vulnerability scanning catches known CVEs in packages you have already installed, but supply chain hardening prevents unknown malicious packages from entering your environment in the first place. This section covers hash pinning with pip's --require-hashes flag, which aborts installation if any downloaded package fails its SHA-256 check against the lockfile record. It also covers configuring a private PyPI mirror using AWS CodeArtifact, Artifactory, or Nexus, and implementing a new-package approval gate that requires security review before any new direct dependency is merged. Together these controls address the typosquatting and dependency confusion attack vectors that vulnerability databases cannot catch because the malicious package has no CVE at the time of installation.

Enforce hash verification in production installs

In your production deployment or CI pipeline: pip install --require-hashes -r requirements.txt. This flag forces pip to verify the SHA-256 hash of every downloaded package against the hash recorded in requirements.txt. If the downloaded package does not match — whether due to a compromised PyPI mirror, a BGP hijack, or a package maintainer backdoor that changes the package content for existing version numbers — the install fails with an error. This is a strong supply chain control with minimal operational overhead when using a lockfile with hashes generated by pip-compile --generate-hashes or Poetry.

Set up a private mirror and require new package approval

For the highest security environments: proxy all PyPI traffic through a private artifact repository (AWS CodeArtifact, Artifactory, Nexus) where each package is vetted before being made available. Implement a new-dependency approval process: any pull request that adds a new package to requirements.in requires a security team review that checks the package's publication history, author reputation, download counts (fresh typosquatted packages have very low counts), and code review of the package's setup.py and __init__.py for malicious install-time code. This manual review gates the introduction of new packages without affecting the iteration speed of existing, approved packages.

Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

The bottom line

Python dependency security requires three things working together: a scanner (pip-audit or OSV-Scanner) integrated into CI that blocks deployments of packages with critical CVEs, a lockfile with hash verification that pins the exact dependency set and prevents supply chain substitution, and a private mirror or new-package approval process that prevents typosquatted or malicious packages from entering your environment. Start with pip-audit in the CI pipeline — it takes 30 minutes to add and immediately surfaces CVEs you did not know existed. Add lockfile with hash pinning to all services within 30 days. Private mirror implementation is a longer project but the right target for any organization handling sensitive data or operating in regulated industries.

Frequently asked questions

How do I use pip-audit to scan my Python project for vulnerabilities?

Install pip-audit: pip install pip-audit. Scan the current environment: pip-audit. Scan a specific requirements file: pip-audit -r requirements.txt. Scan a Poetry project: pip-audit --requirement <(poetry export --without-hashes). Output as JSON for CI integration: pip-audit -r requirements.txt -f json -o audit-results.json. pip-audit queries both the PyPI Advisory Database and OSV (Open Source Vulnerabilities) database, returning a list of vulnerable packages with CVE IDs, affected versions, and available fix versions. In CI/CD: run pip-audit --strict to exit with a non-zero status code when any vulnerability is found, which fails the pipeline and blocks deployment of vulnerable dependencies.

What is the difference between pip-audit, Safety, and OSV-Scanner for Python vulnerability scanning?

pip-audit: official PyPA tool, queries PyPI Advisory Database and OSV, supports requirements.txt and installed environments, free, integrates with GitHub Actions and most CI systems. Ideal for most Python projects. Safety: commercial tool (safety.io), has a free tier with limited CVE database access, paid tier includes full database, provides compliance reports. Previously used the Safety DB but now primarily uses its own curated database. OSV-Scanner: Google's open-source tool, supports Python (requirements.txt, poetry.lock, Pipfile.lock), scans against the broader OSV database which aggregates CVEs from GitHub Advisory Database, NVD, and package-specific advisory databases. Better cross-language support (can scan the entire monorepo including Python, JavaScript, Go, Rust). For Python-only projects: pip-audit is the simplest choice. For polyglot repositories: OSV-Scanner provides unified scanning across all languages.

How do I prevent typosquatted or malicious PyPI packages from being installed?

Defense layers for malicious package prevention: (1) Always use a lockfile (Poetry, pip-tools, or uv) that pins exact versions and hashes — this prevents resolution from ever fetching a newly published package when you run pip install. (2) Enable hash pinning in pip: generate requirements.txt with pip-compile --generate-hashes (from pip-tools) and install with pip install --require-hashes -r requirements.txt — any package whose hash does not match the lockfile causes the install to fail. (3) Configure a private PyPI mirror (Artifactory, Nexus, or AWS CodeArtifact) and add only vetted packages to it — restrict pip to only fetch from your private mirror by setting index-url in pip.conf. (4) In CI/CD: alert on any newly added package in a pull request and require security team review before merging new direct dependencies. (5) Use pip-audit --skip-editable to exclude local packages that should not be scanned.

What is a Python lockfile and why does it matter for security?

A lockfile records the exact versions and hashes of all packages (direct and transitive) that are installed in a working environment. Without a lockfile: pip install resolves dependencies at install time, potentially fetching different versions on different machines or at different points in time — a CVE published in a transitive dependency after your last test run appears silently in your next deployment. With a lockfile (requirements.txt with hashes via pip-compile, poetry.lock, uv.lock, Pipfile.lock): every install uses exactly the versions recorded in the lockfile, making your dependency set auditable and reproducible. Generate for pip-tools: pip-compile requirements.in --generate-hashes -o requirements.txt. Generate for Poetry: poetry lock. Commit the lockfile to version control and update it deliberately (via pip-compile --upgrade or poetry update), which triggers your CI scanner to check for new CVEs in updated versions.

How do I integrate Python dependency scanning into my CI/CD pipeline?

GitHub Actions example for pip-audit: add a job that runs after checkout and dependency installation: name: Security Audit, steps: [checkout, setup Python, install dependencies (pip install -r requirements.txt), run audit (pip-audit -r requirements.txt --strict)]. The --strict flag exits non-zero if any vulnerability is found, failing the pipeline. For a less disruptive initial setup: use pip-audit without --strict (exits 0 even with findings) for the first 30 days to establish a baseline, then switch to --strict after remediating existing findings. GitLab CI equivalent: run pip-audit in a security stage after the build stage, with artifacts configured to save the JSON output report. Combine with Dependabot (GitHub) or Renovate (universal) to receive automated PRs when pip-audit detects a fix is available by upgrading a specific package.

What should I do when pip-audit finds a CVE in a dependency I cannot immediately upgrade?

When an immediate upgrade is not possible: (1) Assess exploitability: read the CVE description and determine whether your application uses the vulnerable code path. A CVE in an HTTP client library that only affects requests made with a specific authentication method may not be exploitable if your application does not use that method. (2) Check for a minimal patch: some packages publish security-only patch releases (e.g., upgrading from 2.1.0 to 2.1.1 fixes the CVE without changing APIs) — try the patch release in a staging environment. (3) Add a documented suppression: if the CVE is not exploitable in your context, add pip-audit --ignore-vuln GHSA-xxxx to your CI configuration with a comment documenting why — this prevents the CI from blocking deployments on a known, accepted risk while maintaining awareness of its existence. (4) File a ticket and set a remediation deadline — CVEs in dependencies without a fix plan become compliance findings.

How do I set up a private PyPI mirror to reduce supply chain risk?

A private PyPI mirror (also called an artifact repository) proxies PyPI packages through your controlled infrastructure, enabling you to: scan packages before making them available to developers, block packages that are not on an approved list, and cache packages for air-gapped or restricted environments. Options: AWS CodeArtifact (managed, supports PyPI format, integrates with IAM), JFrog Artifactory, Sonatype Nexus Repository, or DevPi (open source, self-hosted). Setup with AWS CodeArtifact: create a CodeArtifact domain and repository with a PyPI upstream, configure pip to use it: pip config set global.index-url https://your-domain.d.codeartifact.region.amazonaws.com/pypi/your-repo/simple/. Scan packages with your SCA tool before adding them to the approved list. Enforce the private mirror in CI by setting PIP_INDEX_URL in CI environment variables and blocking direct PyPI access via firewall rules in your CI environment.

Sources & references

  1. pip-audit: Python Package Vulnerability Scanner
  2. OSV-Scanner: Open Source Vulnerability Scanner
  3. PyPI Malware Research
  4. Python Packaging User Guide: Security

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.