Scan Python packages for supply chain attacks before installing them
Project description
pipguard
Python supply chain security tool. Scan packages before installing them.
pip install pipguard
pipguard install litellm==1.82.8 # Blocks the March 2026 attack. Exits 1.
Zero configuration. Zero external dependencies. Pure stdlib.
The Problem
The March 2026 litellm attack (97M downloads/month) embedded Python code in a .pth
file — executed automatically at interpreter startup, exfiltrating SSH keys, AWS credentials,
and Kubernetes configs from a single pip install.
Classical tools (pip-audit, GuardDog) are blind to zero-day attacks. They check known signatures. pipguard asks a different question:
Should any
pip installbe allowed to read~/.ssh/id_rsa?
The answer is no. And that question doesn't require a database.
Installation
pip install pipguard
Usage
# Install a single package
pipguard install requests
# Install from requirements.txt
pipguard install -r requirements.txt
# CI mode: never prompts, exits 1 on CRITICAL/HIGH
pipguard install --yes -r requirements.txt
# Allow a known-legitimate package that accesses credentials
pipguard install --allow paramiko paramiko
# Override for known false-positives (use with care)
pipguard install --force my-trusted-internal-pkg
How It Works
pipguard install X
│
▼
pip download --prefer-binary X ← downloads wheel/sdist, no code execution
│
▼
Detect sdist fallback ← exit 2 if sdist detected (unless --allow-sdist)
│
▼
Extract archive (zipfile/tarfile) ← never executes code
│
▼
AST scan all .py files ← parallel, ThreadPoolExecutor
setup.py, pyproject.toml, *.pth ← CRITICAL/HIGH scope
all other .py ← MEDIUM/LOW scope
│
▼
Risk scoring:
CRITICAL → block (exit 1)
HIGH → block (exit 1)
MEDIUM → warn + confirm
LOW → warn + confirm
CLEAN → install silently
│
▼
pip install --no-index ← installs FROM SCANNED FILES (TOCTOU-safe)
--find-links /tmp/pipguard-XX
Risk Levels
| Level | Triggers |
|---|---|
| CRITICAL | .pth executable code; eval(base64.b64decode(...)); network in setup.py |
| HIGH | Reads ~/.ssh, ~/.aws, ~/.kube, ~/.gnupg in install hooks; shell=True subprocess |
| MEDIUM | Network calls in runtime code; sensitive env var access (*TOKEN*, *KEY*, etc.) |
| LOW | Dynamic importlib/__import__ |
| CLEAN | None of the above |
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Clean install succeeded |
| 1 | Blocked — CRITICAL or HIGH risk detected |
| 2 | Scan error (download failed, unsupported format) |
GitHub Action
- name: Secure pip install
uses: pipguard/action@v1
with:
requirements: requirements.txt
Seed Allowlist
These packages legitimately access credential stores and are pre-allowlisted (HIGH reduced to MEDIUM — CRITICAL is never reduced):
keyring, keyrings.alt, boto3, botocore, awscli, paramiko,
google-auth, google-cloud-storage, google-cloud-bigquery,
google-cloud-core, azure-identity
Add more per-invocation: pipguard install --allow my-package ...
Limitations (Phase 1)
- Static AST scanning can be bypassed by multi-layer obfuscation
- C extensions (
.so/.pyd) are opaque to AST scanning (flagged as UNKNOWN) - Python/pip only — no npm, cargo, go modules
- Phase 2 (in design): seccomp/eBPF sandbox for zero-day capability interception
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pipguard-0.1.0.tar.gz.
File metadata
- Download URL: pipguard-0.1.0.tar.gz
- Upload date:
- Size: 19.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe758436c2f94acc5079af056fbce153aaa93b01c423c3e8b0537a6669e32836
|
|
| MD5 |
b9ba45fe8b6693b0352ceb4d449a6b63
|
|
| BLAKE2b-256 |
b0abba8cf9be7d10e363ab84d0133e2410242f48182860143ec452c39666f430
|
Provenance
The following attestation bundles were made for pipguard-0.1.0.tar.gz:
Publisher:
publish.yml on shenxianpeng/pipguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pipguard-0.1.0.tar.gz -
Subject digest:
fe758436c2f94acc5079af056fbce153aaa93b01c423c3e8b0537a6669e32836 - Sigstore transparency entry: 1180347039
- Sigstore integration time:
-
Permalink:
shenxianpeng/pipguard@6d18a6f00db3344d7a237d1d5eb4e8ca6dc6ba88 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/shenxianpeng
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6d18a6f00db3344d7a237d1d5eb4e8ca6dc6ba88 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pipguard-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pipguard-0.1.0-py3-none-any.whl
- Upload date:
- Size: 16.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c1cd003dc6c8664c258d5332aac7b52fd5ef525bae5f58faf0130931af7b64e
|
|
| MD5 |
570328256bf2f48a8dc9ee756fe7a4da
|
|
| BLAKE2b-256 |
f95bab8b7a2b13fa19f930ebfa41cca3a4b845ae15f6fbc1c60430f96cd8917a
|
Provenance
The following attestation bundles were made for pipguard-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on shenxianpeng/pipguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pipguard-0.1.0-py3-none-any.whl -
Subject digest:
3c1cd003dc6c8664c258d5332aac7b52fd5ef525bae5f58faf0130931af7b64e - Sigstore transparency entry: 1180347084
- Sigstore integration time:
-
Permalink:
shenxianpeng/pipguard@6d18a6f00db3344d7a237d1d5eb4e8ca6dc6ba88 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/shenxianpeng
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6d18a6f00db3344d7a237d1d5eb4e8ca6dc6ba88 -
Trigger Event:
push
-
Statement type: