Skip to content

How to Vet a Python Package Before Installing It

litellm 1.82.7 and 1.82.8, and Lightning AI 2.6.2 and 2.6.3, hit PyPI before matching source releases appeared on GitHub. A 30-second tag check would have caught either one. Use this five-minute pre-install routine to catch that kind of Python supply chain attack before adding a package to pyproject.toml.

Read the PyPI signals

Open the package page on PyPI and check four things.

  • Download trend. Run uvx with pypistats to see recent download counts:

    $ uvx pypistats recent requests
    ┌────────────┬───────────────┬─────────────┐
    │   last_day │    last_month │   last_week │
    ├────────────┼───────────────┼─────────────┤
    │ 52,751,217 │ 1,497,550,436 │ 343,955,728 │
    └────────────┴───────────────┴─────────────┘
    

    Stars and download totals can be inflated by bot traffic and CI runs, so weight them lightly.

  • Release cadence. The PyPI sidebar shows release history. Look for steady releases over a year or more, not a single recent burst followed by silence (or vice versa).

  • Maintainer activity. The PyPI page links to the maintainer’s other projects. A single-package account with a recent first release deserves more scrutiny than a maintainer with several older projects.

  • Last-release date. A package whose last release was years ago may still be fine if the API surface is stable, but combine that with no recent commits on the source repo and you’re depending on unmaintained code.

Verify PyPI matches the source repo

A PyPI release with no matching tag in the source repo is the signature of a real attack pattern, not a theoretical one. Of every check in this guide, this is the one to do first.

Click “Project links” on the PyPI page and confirm the “Source” link points at a live repository. Compare the latest PyPI version to the latest tag or release on the source repo. If PyPI lists a version that has no matching tag in Git, treat the release as compromised until proven otherwise.

A PEP 740 attestation from Trusted Publishing is the cryptographic version of this check, tying the artifact on PyPI to the workflow run that built it. Coverage is still partial, so a missing attestation isn’t evidence of compromise, but a present and valid one is strong evidence of a clean release path.

Skim the recent commits

Don’t try to read the whole source. Read what changed since the last version you trusted, or in the past two weeks if the package is new to you:

git log --since="2 weeks ago" --stat

PyPI’s release page also shows file changes between versions if the package uploads sdists.

What to scan for, focused on why installing a Python package can run code:

  • Install-time hooks: a setup.py with a custom cmdclass, new .pth files, or import-time network calls in __init__.py.
  • Suspicious URLs or base64 blobs that decode to executable code.
  • New subprocess, os.system, eval, or exec calls in package init.
  • Obfuscated strings or fetch-and-execute patterns near install time.

Many malicious PyPI uploads use one of these patterns to run code at install or first import. Spotting any of them in a diff is reason enough to back out and ask questions.

Check reputation tools

A handful of services aggregate signals across PyPI, GitHub, and vulnerability databases. They miss things, but they take seconds to consult.

  • Socket scans dependencies for malicious behavior and surfaces 70+ signals across supply chain risk, vulnerabilities, quality, maintenance, and licensing. PyPI packages have a page at https://socket.dev/pypi/package/<name>.
  • Snyk Advisor computes a package health score across popularity, maintenance, security, and community, and labels packages “healthy,” “sustainable,” or “risky.” Each Python package has a page at https://snyk.io/advisor/python/<name>.
  • deps.dev is Google’s package insights service across major language ecosystems. It surfaces the dependency graph, OSV advisories, and the OpenSSF Scorecard for the linked source repo. PyPI packages have a page at https://deps.dev/pypi/<name>.

Treat each as a smoke detector, not a verdict. A clean Snyk score on a package whose PyPI release doesn’t match any GitHub tag is still suspicious.

Know what this checklist won’t catch

A small, careful malicious diff in a high-traffic package will pass every step here. The maintainer’s account gets compromised, the attacker pushes a tag and a release together, the diff is small enough that nobody notices for hours, and Socket and Snyk haven’t scored it yet. That is the litellm and Lightning AI playbook, and the manual checklist isn’t enough on its own.

That’s why the project-side defenses are the backstop:

Learn more

Last updated on