Catch install-time PyPI malware before pip install runs it. Zero dependencies, one file, MIT.
python pypi_supply_scan.py requests
# 🟢 requests 2.34.2 [SAFE]
python pypi_supply_scan.py some-sketchy-package
# 🔴 some-sketchy-package 1.0.0 [CRITICAL]
# CRITICAL Network call during install (setup.py:14)
# CRITICAL Decode-then-execute chain (setup.py:15)pip install foo executes foo's setup.py to build and install it. A legitimate setup.py
only declares metadata — but nothing stops a malicious one from opening a network connection,
spawning a shell, exec-ing an obfuscated blob, or reading your ~/.ssh and *_SECRET env vars
the moment you install it, as your user. That is the entire PyPI supply-chain attack surface,
and it runs before any of your own code does.
This tool downloads a package's sdist without executing it and scans setup.py for those
install-time behaviors. Because a real setup.py never phones home or runs code, the signal is
unusually clean — far more precise than a general code scan where subprocess is normal.
| Signature | Severity |
|---|---|
Network call at install (requests/urllib/socket/…) |
CRITICAL |
Dynamic code exec (exec/eval/marshal.loads/__import__('…')) |
CRITICAL |
Obfuscated payload (base64.b64decode/bytes.fromhex/\x.. blobs) |
CRITICAL |
Decode-then-execute chain (exec(base64…) |
CRITICAL |
Download-and-run (curl … | sh, urlretrieve+run) |
CRITICAL |
Reads credentials/secrets (*_SECRET/~/.ssh/id_rsa/~/.aws/credentials/browser cookies) |
HIGH |
Process spawn (os.system/subprocess.Popen/…) |
HIGH |
Most supply-chain scanners get muted within a week because they flag every subprocess and every
env-var read. This one is tuned on both sides:
- A normal
setup.py— including git-based versioning (subprocess.Popen(["git","rev-list",…])), build flags (os.environ["CMAKE_ARGS"],os.getenv("RELEASE_VERSION")), and dependency names (install_requires=["keychain"]) — scores 0. Those are the three false-positive traps that make naive scanners useless, and they are explicitly suppressed. - Five real-world malware patterns (install-time exfiltration,
exec(base64…), subprocess download, acmdclasspost-install hook, a curl-pipe-to-shell) all trip at CRITICAL/HIGH.
If a scanner can't pass a clean setup.py, its silence on the real threats means nothing.
# scan named packages (exit code 2 if any is CRITICAL — CI-friendly)
python pypi_supply_scan.py requests numpy some-package
# find + scan typosquats of packages you depend on (the #1 real vector: a typo fetches malware)
python pypi_supply_scan.py --typosquat requests django discord
# scan the freshly-published feed (where malware lands before removal)
python pypi_supply_scan.py --recent 50
# machine-readable
python pypi_supply_scan.py --json requestspython pypi_supply_scan.py $(python - <<'EOF'
import tomllib,sys
# print your top-level deps, one per line, then feed them to the scanner
EOF
) || { echo "install-time risk found"; exit 1; }Scanning the fresh-upload feed usually shows ~100% clean at any instant — PyPI removes malware
fast. The durable risk isn't the registry being "full of malware"; it's a typo fetching one of
the typosquat slots that are already claimed. Run --typosquat on your own dependencies and see
how many one-keystroke-away names already exist. For 42 popular libraries, 138 typosquat-named
packages exist right now (dango, crytography, aihttp, ddiscord, bitcoin-cli, …).
- Scan
setup.pybefore install (this tool) and refuse on CRITICAL. - Prefer wheels:
pip install --only-binary :all:— nosetup.pyexecuted at install. - Pin + hash:
pip install --require-hashesso a compromised new release of a trusted name can't silently swap in. - Allowlist exact names so a typo fails closed instead of fetching a loaded slot.
- Sandbox installs of untrusted packages — no credentials, no
~/.ssh, no egress beyond the index.
- Hosted, no-install scanning and a security-and-web-data API suite for agents (pay-per-call, no signup): the same precision engine behind this tool runs as an x402 API — eltociear-skill-audit.hf.space / eltociear-tokenguard.hf.space.
- Hosted on the Apify Store (Repo Security Scanner) — the supply-chain engine as a no-install Actor: scan a GitHub org or repo list, pay per repo.
- A deeper writeup — the six signatures, the false-positive taxonomy, and a calibrated scan of 259 real packages — is available as a research report.
MIT — see LICENSE. Findings are pattern matches that warrant human review, not verdicts; confirmed malware should be reported to PyPI security, never published as an unverified accusation.