Safeguard
Open Source Security

What is PyPI Security

PyPI security stops typosquats, dependency confusion, and poisoned CI builds before pip install ever runs your code.

Priya Mehta
DevSecOps Engineer
7 min read

PyPI hosts over 600,000 projects and serves billions of package downloads every month, making it the default distribution point for nearly every Python dependency an organization runs in production. That scale is also why it's a favorite target: attackers have used typosquatting, dependency confusion, compromised maintainer accounts, and poisoned CI pipelines to slip malware into the same index that pip install trusts by default. In December 2022, a single malicious upload compromised PyTorch's nightly build pipeline. In May 2023, PyPI had to shut down new account and project registration for hours because automated malware uploads overwhelmed its moderation queue. In December 2024, a popular computer-vision package with millions of downloads shipped a cryptominer for several days before anyone noticed. PyPI security is the set of practices, controls, and tooling that stop these packages from reaching your build servers and production environments in the first place.

What Is PyPI Security?

PyPI security is the discipline of verifying that every package pulled from the Python Package Index is what it claims to be, comes from who it claims to come from, and doesn't execute unwanted code during install or import. Unlike a compiled binary you can sandbox before running, a Python package can execute arbitrary code the moment pip install runs setup.py, because PyPI has historically allowed arbitrary build scripts. That means the attack surface starts before your application ever imports the library — it starts at pip install. PyPI security covers package provenance (was this really published by the maintainer?), integrity (has this file been tampered with since publish?), and behavior (does this package phone home, drop a payload, or read your environment variables?). Organizations that only scan for known CVEs in already-published packages are covering roughly half the problem; the other half is catching malicious packages that were never assigned a CVE because they were malware from day one, not a vulnerable library.

How Do Attackers Compromise PyPI Packages?

Attackers compromise PyPI packages through four repeatable techniques: typosquatting, dependency confusion, maintainer account takeover, and build-pipeline poisoning. Typosquatting relies on developers mistyping a package name (reqeusts instead of requests) or copying a name from an outdated tutorial. Dependency confusion exploits internal package names that also exist, unclaimed, on public PyPI, so a public upload gets pulled instead of the private one your CI expects. Maintainer account takeover happens when an attacker phishes or credential-stuffs their way into an existing, trusted project and pushes a new, malicious version to millions of existing users — no rename, no new install command required. Build-pipeline poisoning is newer and harder to catch: attackers compromise the CI system that builds and publishes a legitimate package, so the source code in the repository stays clean while the artifact published to PyPI is not. The December 2024 compromise of the ultralytics YOLO package followed this last pattern — versions 8.3.41 and 8.3.42 were built through a poisoned GitHub Actions cache and shipped a cryptomining payload to users who had no reason to distrust a package with over 60 million lifetime downloads.

What Is Dependency Confusion, and How Did It Hit PyTorch?

Dependency confusion is an attack where a public package registry serves a malicious package in place of an internal, private one because the internal name was never registered publicly. On December 25, 2022, this technique hit PyTorch directly: Meta's PyTorch team published nightly builds that depended on an internal package called torchtriton, but that name had never been claimed on public PyPI. An attacker registered torchtriton on PyPI and uploaded a malicious version that, per the PyTorch team's own disclosure, collected system information, environment variables, and SSH keys, then exfiltrated them and attempted to establish reverse-shell access. Anyone who ran pip install torch-nightly (or any dependent package) between December 25 and December 30, 2022 pulled the malicious package instead of the internal one, because pip's default resolver has no concept of "internal" versus "public" — it just finds the highest matching version wherever it looks first. PyTorch's fix was to register a placeholder torchtriton package on PyPI so the name could never be squatted again, which is now the standard mitigation any organization with internally named packages should apply.

How Is PyPI Defending Against Malicious Packages?

PyPI is defending against malicious packages primarily through account-security mandates and publishing changes, not content scanning at upload time. Starting with the top 1% of projects by download count in 2022 and expanding to every maintainer account by January 1, 2024, PyPI made two-factor authentication mandatory, closing the credential-stuffing and password-reuse paths that enabled several high-profile account takeovers. In April 2023, PyPI shipped Trusted Publishers, which lets a project authenticate a release directly from a CI provider like GitHub Actions using short-lived OpenID Connect tokens instead of a long-lived API token that can leak in logs or get phished. On May 20, 2023, PyPI's moderators temporarily disabled new user registration and new project creation for about ten hours after a wave of automated malware uploads — using freshly created accounts to publish freshly created packages — outpaced what their review process could triage manually. These are real improvements, but none of them scan package contents for malicious behavior before publish; PyPI relies heavily on post-publish user reports and third-party researchers (Checkmarx, Phylum, Socket, ReversingLabs) to catch and report packages that pass every account-security check but are malicious anyway.

How Can Security Teams Protect Their Python Supply Chain?

Security teams protect their Python supply chain by controlling what gets installed, not just scanning what already got installed. That means pinning exact versions with hashes in requirements.txt or a lockfile so a compromised new release can't silently replace a known-good one, registering placeholder names for every internal package to close the dependency-confusion gap PyTorch hit in 2022, and enforcing Trusted Publisher–based releases for any internal package your own teams publish. It also means treating "new package version" as an event worth reviewing, not an automatic CI trigger — the ultralytics compromise and the 2022 W4SP Stealer campaign, which Checkmarx and Phylum tracked across more than 100 typosquatted PyPI packages exfiltrating Discord tokens and browser credentials, both relied on automated pipelines pulling new versions the moment they published. Static CVE scanning alone won't catch either case, because neither torchtriton nor the poisoned ultralytics builds had a CVE at the time they were doing damage — they were malicious code, not vulnerable code, and most SCA tools are tuned to find the latter.

How Safeguard Helps

Safeguard closes the gap that CVE-only scanning leaves open by combining reachability analysis, Griffin AI, SBOM automation, and auto-fix PRs into one pipeline built for exactly these PyPI attack patterns. Reachability analysis tells your team whether a flagged package's vulnerable or suspicious code path is actually called from your application, so a typosquat sitting unused in a transitive dependency tree doesn't trigger the same alarm as one your service imports directly. Griffin AI continuously analyzes newly published and updated PyPI packages for behavioral red flags — obfuscated install scripts, unexpected network calls, credential harvesting patterns — the same signals that would have flagged torchtriton and the W4SP Stealer packages before they reached production. Safeguard generates and ingests SBOMs automatically so every Python dependency, including internal package names vulnerable to confusion attacks, is inventoried and monitored continuously rather than checked once at release time. When a risky package is identified, Safeguard opens an auto-fix pull request with a pinned, verified replacement version, cutting remediation from a manual triage ticket down to a single review-and-merge.

Never miss an update

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