
Python Wheel File Security Scanner — scan .whl files for security issues before installation. Detects path traversal (CVE-2026-24049), RECORD tampering, command shadowing, secrets, zip bombs. Zero deps.
Python Wheel File Security Scanner — scan .whl files for security issues before installation.
Motivated by recent CVEs in Python wheel handling:
Zero dependencies. Single file. Python 3.9+.
pip-audit checks if your packages have known CVEs. wheelaudit checks if a wheel file itself is structurally malicious — path traversal, tampered RECORD hashes, embedded secrets, command shadowing, zip bombs, and more. It's the difference between "is this package known-bad?" and "does this package look bad?"
Run it before pip install. Run it in CI. Run it on downloaded wheels you don't trust.
# Just copy the file
curl -O https://raw.githubusercontent.com/kriskimmerle/wheelaudit/main/wheelaudit.py
chmod +x wheelaudit.py
# Or clone the repo
git clone https://github.com/kriskimmerle/wheelaudit.git
cd wheelaudit
# Scan a wheel file
python3 wheelaudit.py package-1.0.0-py3-none-any.whl
# Scan multiple wheels
python3 wheelaudit.py *.whl
# Scan a directory of wheels
python3 wheelaudit.py ./wheels/
# CI mode: exit 1 if score below threshold
python3 wheelaudit.py package.whl --check --min-score 80
# JSON output for tooling integration
python3 wheelaudit.py package.whl --json
# Verbose mode with details
python3 wheelaudit.py package.whl -v
# Filter by severity
python3 wheelaudit.py package.whl --severity high
# Ignore specific rules
python3 wheelaudit.py package.whl --ignore WH11
# Read from stdin
cat package.whl | python3 wheelaudit.py --stdin
| Rule | Severity | Description |
|---|---|---|
| WH01 | CRITICAL | Path traversal in filenames (../, absolute paths, null bytes) |
| WH02 | CRITICAL-HIGH | RECORD file integrity (missing, tampered hashes, unlisted files) |
| WH03 | HIGH-LOW | Suspicious file types (executables, pickle, archives, hidden files) |
| WH04 | CRITICAL | Entry point command shadowing (pip, python, sudo, etc.) |
| WH05 | CRITICAL | Embedded secrets (API keys, tokens, private keys) |
| WH06 | CRITICAL-LOW | File permission issues (SUID/SGID, world-writable, executable data) |
| WH07 | HIGH-MEDIUM | Metadata consistency (missing METADATA/WHEEL, structural issues) |
| WH08 | HIGH | Zip bomb detection (extreme compression ratios, oversized archives) |
| WH09 | HIGH-MEDIUM | PEP 427 compliance (filename format, name/version mismatch) |
| WH10 | CRITICAL | Namespace collision (stdlib module shadowing) |
| WH11 | HIGH-MEDIUM | Malicious code patterns (eval, exec, os.system, credential access) |
| WH12 | MEDIUM-LOW | Data file issues (scripts in .data/, oversized files) |
| WH13 | HIGH | CRC32 manipulation (CVE-2025-1889 bypass pattern) |
| WH14 | HIGH | Extension mismatch (ELF/PE binary disguised as .py/.txt) |
| WH15 | HIGH-MEDIUM | Symlinks and duplicate filenames |
CRITICAL (20 points): Path traversal, SUID bits, embedded secrets, command shadowing, stdlib shadowing, RECORD hash tampering
HIGH (12 points): Missing RECORD, suspicious executables, zip bombs, CRC manipulation, extension mismatch, symlinks, credential harvesting patterns
MEDIUM (6 points): Backslash paths, missing metadata, PEP 427 issues, subprocess/network patterns
LOW (2 points): Hidden files, executable data files, large files
INFO (0 points): Legitimate native extensions, environment variable access
============================================================
requests 2.32.5 — B (88/100)
============================================================
[HIGH]
WH11 Suspicious pattern: __import__() — dynamic import (requests/packages.py)
Summary: 1 high
============================================================
evil-pip 99.0.0 — F (0/100)
============================================================
[CRITICAL]
WH01 Path traversal in wheel archive (../../../etc/crontab)
WH04 Entry point shadows system command: 'pip'
WH04 Entry point shadows system command: 'python'
WH05 Embedded secret: GitHub Personal Access Token (pip/__init__.py)
[HIGH]
WH02 File not listed in RECORD (../../../etc/crontab)
WH03 Suspicious file type: .exe (pip/payload.exe)
WH11 Suspicious pattern: os.system() — shell command execution
WH11 Suspicious pattern: eval() call — dynamic code execution
WH11 Suspicious pattern: SSH directory reference
Summary: 4 critical, 5 high, 2 medium, 2 low
{
"version": "0.1.0",
"results": [
{
"path": "package-1.0.0-py3-none-any.whl",
"score": 88,
"grade": "B",
"metadata": {
"name": "package",
"version": "1.0.0",
"file_count": 15,
"total_uncompressed_bytes": 45000
},
"findings": [
{
"rule": "WH11",
"severity": "high",
"message": "Suspicious pattern: __import__() — dynamic import",
"file": "package/compat.py"
}
],
"summary": {
"high": 1
}
}
]
}
- name: Audit wheel
run: |
python3 wheelaudit.py dist/*.whl --check --min-score 80
pip download some-package --no-deps -d /tmp/wheels
python3 wheelaudit.py /tmp/wheels/*.whl --check
pip install /tmp/wheels/*.whl
wheelaudit treats every .whl file as an untrusted zip archive. It:
No files are extracted to disk. All analysis happens in memory.
| Feature | wheelaudit | pip-audit | safety | GuardDog |
|---|---|---|---|---|
| Zero deps | ✅ | ❌ | ❌ | ❌ |
| Scans wheel contents | ✅ | ❌ | ❌ | ✅ |
| Path traversal detection | ✅ | ❌ | ❌ | ❌ |
| RECORD integrity | ✅ | ❌ | ❌ | ❌ |
| CVE database | ❌ | ✅ | ✅ | ❌ |
| Offline operation | ✅ | ❌ | ❌ | ❌ |
| Python 3.9+ | ✅ | ✅ | ✅ | ✅ |
wheelaudit complements pip-audit/safety — they check known vulnerabilities, wheelaudit checks the wheel itself.
MIT