Supply chain compromise scanner — detects known PyPI and npm attacks via data-driven threat profiles
Project description
Supply Chain Compromise Scanner
A data-driven scanner that detects indicators of compromise (IOCs) from known PyPI and npm supply chain attacks. Threat profiles are defined in TOML files and are user-extensible — add your own without writing code.
Built-in threat profiles:
| ID | Package | Ecosystem | Compromised Versions | Date |
|---|---|---|---|---|
litellm-2026-03 |
litellm | PyPI | 1.82.7, 1.82.8 | 2026-03-24 |
axios-2026-03 |
axios | npm | 1.14.1, 0.30.4 | 2026-03-31 |
No guarantees. This tool attempts to find known artifacts associated with supply chain compromises. It does not guarantee detection of all malicious activity, nor does it guarantee that a clean scan means your system was not affected. A determined attacker may have removed traces, and the tool cannot detect secrets that have already been exfiltrated. Use this scanner as one input in your incident response — not as a definitive verdict.
Quick Start
Option A: Install from PyPI
Linux / macOS:
# Recommended — installs in an isolated environment
pipx install scan-litellm-compromise
scan-litellm
If you don't have pipx: sudo apt install pipx (Debian/Ubuntu) or brew install pipx (macOS).
Alternatively, using pip inside a virtual environment:
python3 -m venv /tmp/scanner && /tmp/scanner/bin/pip install scan-litellm-compromise
/tmp/scanner/bin/scan-litellm
Note:
pip installdirectly will fail on modern Linux distributions (Debian 12+, Ubuntu 23.04+, Fedora 38+) due to PEP 668. Usepipxor a virtual environment instead.
Windows:
pip install scan-litellm-compromise
scan-litellm
Or using a virtual environment:
py -m venv %TEMP%\scanner && %TEMP%\scanner\Scripts\pip install scan-litellm-compromise
%TEMP%\scanner\Scripts\scan-litellm
Option B: Run from source (no install needed)
Linux / macOS:
git clone https://github.com/CognitiveSand/scan_litellm_compromise.git
cd scan_litellm_compromise
python3 run_scan.py
Windows — double-click run_scan.bat, or from a terminal:
git clone https://github.com/CognitiveSand/scan_litellm_compromise.git
cd scan_litellm_compromise
py run_scan.py
No dependencies required — uses only the Python 3.11+ standard library.
Command-Line Options
| Flag | Description |
|---|---|
| (no flags) | Scans all known threats (default behavior). |
--threat ID |
Scan for a specific threat only (e.g. --threat litellm-2026-03). |
--threat-file PATH |
Load a custom threat profile from a TOML file. |
--list-threats |
List all available threat profiles and exit. |
--scan-path DIR |
Restrict scanning to a specific directory instead of system-wide search. |
--resolve-c2 |
Enable live DNS queries to C2 domains (default: use known IPs only). |
--help |
Show usage information. |
Warning about
--resolve-c2: This flag causes the scanner to make live DNS queries to attacker-controlled domains. This carries real risks:
- Operational security: Your DNS query is visible to the attacker's infrastructure.
- Network monitoring: The queries may trigger alerts in your SIEM/IDS.
In most cases you do not need this flag. The scanner ships hardcoded known IPs confirmed by multiple threat intelligence sources.
Examples:
# Scan for all known threats (default)
scan-litellm
# Scan only a specific project directory
scan-litellm --scan-path /home/user/myproject
# Scan for a specific threat only
scan-litellm --threat axios-2026-03
# List available threat profiles
scan-litellm --list-threats
# Use a custom threat profile
scan-litellm --threat-file ./my-threat.toml
Threat Library
Threat profiles are TOML files that define everything about a specific supply chain attack: package name, ecosystem, compromised versions, C2 infrastructure, IOC file paths, and remediation steps.
Built-in threats
Ship with the package in scan_litellm_compromise/threats/. Updated via pip install --upgrade.
User-defined threats
Drop a .toml file into:
- Linux/macOS:
~/.config/scan-supply-chain/threats/ - Windows:
%LOCALAPPDATA%\scan-supply-chain\threats\
User profiles override built-in profiles with the same id.
Writing a threat profile
[threat]
id = "mypackage-2026-04"
name = "MyPackage Supply Chain Attack"
date = "2026-04-01"
ecosystem = "pypi" # "pypi" or "npm"
package = "mypackage"
compromised = ["1.0.1"]
safe = "1.0.0"
advisory = "https://example.com/advisory"
description = "Description of what happened."
[c2]
domains = ["evil.example.com"]
ports = []
[c2.ips]
"evil.example.com" = ["1.2.3.4"]
[[ioc.known_paths]]
description = "backdoor payload"
linux = ["/tmp/backdoor.py"]
darwin = ["/tmp/backdoor.py"]
windows = ['%TEMP%\backdoor.exe']
[ioc.phantom_deps]
names = ["malicious-dep"] # should NEVER exist
[ioc.kubernetes]
pod_patterns = []
namespace = ""
[ioc.windows]
registry_keywords = []
schtask_keywords = []
[remediation]
rotate_secrets = true
install_command = "pip install mypackage==1.0.0"
[remediation.remove_artifacts]
linux = ["Remove /tmp/backdoor.py"]
darwin = ["Remove /tmp/backdoor.py"]
windows = ['Remove %TEMP%\backdoor.exe']
[remediation.check_persistence]
linux = ["Check crontab -l"]
darwin = ["Check launchctl list"]
windows = ["Check Task Scheduler"]
Video Overview
This scanner was inspired by Fahd Mirza's video highlighting the LiteLLM incident — thanks to him for bringing it to attention.
Known Attacks
LiteLLM PyPI Compromise (March 24, 2026)
Two backdoored versions of the litellm Python package were published to PyPI:
- v1.82.7 — malicious code injected into
proxy_server.py(base64-encoded) - v1.82.8 — added a
litellm_init.pthfile that executes on every Python interpreter startup
Available for ~3 hours before PyPI quarantined them. LiteLLM has ~95 million monthly downloads. The attacker compromised BerriAI's PyPI token by poisoning the aquasecurity/trivy-action GitHub Action.
Three-stage payload: credential harvesting, AES-256+RSA-4096 encrypted exfiltration to C2, persistent backdoor polling every 50 minutes. If K8s credentials found, deploys privileged node-setup-* pods for lateral movement.
Axios npm Compromise (March 31, 2026)
The axios npm package (100M weekly downloads) was compromised via maintainer account takeover attributed to North Korea's UNC1069/BlueNoroff:
- v1.14.1 (tagged
latest) and v0.30.4 (taggedlegacy) - Injected phantom dependency
plain-crypto-js@4.2.1with postinstall RAT dropper - Cross-platform RAT payloads: PowerShell (Windows), compiled binary (macOS), Python script (Linux)
- Self-deleting dropper with double-layer obfuscation
Available for ~3 hours. C2 at sfrclak.com:8000.
What This Scanner Detects
The scanner runs a 5-phase pipeline for each threat profile:
| Phase | What It Looks For |
|---|---|
| 1 | Package metadata directories across the filesystem (PyPI: dist-info/egg-info; npm: node_modules/*/package.json) |
| 2 | Package version from metadata — flags compromised versions |
| 3 | IOC artifacts: backdoor files, persistence mechanisms, temp staging files, C2 network connections, suspicious Kubernetes pods, phantom dependencies, Windows Registry/Tasks |
| 4 | Source files and dependency configs referencing the package, flagging pinned compromised versions |
| 5 | Per-threat summary, verdicts, and remediation guidance |
Limitations
- Cannot detect exfiltrated secrets. Credential rotation is required regardless of scan results.
- Artifacts may have been cleaned up. Absence of IOC files does not prove the system was never compromised.
- Does not inspect Docker image layers, CI/CD runner caches, or remote systems.
- Root-owned paths (
/rooton Linux) are excluded. - Windows Registry scanning is best-effort.
Platform Support
| Feature | Linux | macOS | Windows 10/11 |
|---|---|---|---|
| Package detection | /home, /opt, /usr, /srv, /var |
/Users, /opt/homebrew, /usr/local, /Library |
%USERPROFILE%, %APPDATA%, Program Files |
| Conda/pipx/nvm detection | /opt/conda, ~/.local/share/pipx |
Homebrew Caskroom, ~/.local/share/pipx |
%LOCALAPPDATA%\Miniconda3, %LOCALAPPDATA%\pipx |
| Network connections | ss -tnp |
lsof -i -P -n |
netstat -ano |
| ANSI terminal colors | Native | Native | Auto-enabled via Virtual Terminal Processing |
If Compromise Is Detected
Assume ALL secrets on the affected machine are compromised. The scanner provides threat-specific remediation steps. General guidance:
- Rotate credentials immediately — SSH keys, cloud credentials, API keys, database passwords, CI/CD tokens
- Remove malicious artifacts — see scanner output for specific paths
- Fix the package — install the safe version indicated by the scanner
- Update pinned versions — check all dependency files for compromised version pins
- Block C2 domains — at DNS/firewall level
- Audit cloud provider logs — check for unauthorized API calls
Advisories and References
LiteLLM
| ID | Source |
|---|---|
| PYSEC-2026-2 | Python Packaging Authority (PyPA) |
| SNYK-PYTHON-LITELLM-15762713 | Snyk |
| GitHub Issue #24512 | Initial disclosure |
Research: Endor Labs, Datadog Security Labs, Snyk, Sonatype, StepSecurity
Axios
| ID | Source |
|---|---|
| GHSA-fw8c-xr5c-95f9 | GitHub Advisory |
| MAL-2026-2306 | Malicious Package Identifier |
Research: Snyk, Socket, Huntress, Datadog Security Labs, Wiz
Known IOC Hashes (SHA-256)
| File | SHA-256 |
|---|---|
litellm_init.pth (v1.82.8) |
71e35aef03099cd1f2d6446734273025a163597de93912df321ef118bf135238 |
proxy_server.py (v1.82.7) |
a0d229be8efcb2f9135e2ad55ba275b76ddcfeb55fa4370e0a522a5bdee0120b |
sysmon.py (dropped backdoor) |
6cf223aea68b0e8031ff68251e30b6017a0513fe152e235c26f248ba1e15c92a |
Project Structure
scan_litellm_compromise/
threats/ Threat profile TOML files (user-extensible)
litellm-2026-03.toml LiteLLM PyPI compromise
axios-2026-03.toml Axios npm compromise
threat_profile.py ThreatProfile dataclass + TOML loader
ecosystem_base.py EcosystemPlugin protocol + factory
ecosystem_pypi.py PyPI: dist-info, METADATA, Python patterns
ecosystem_npm.py npm: node_modules, package.json, JS/TS patterns
scanner.py Orchestrator (multi-threat CLI)
config.py Generic constants (skip dirs)
models.py Typed data structures
formatting.py Terminal output (ANSI with Windows support)
platform_policy.py Platform abstraction (Strategy pattern)
platform_linux.py Linux paths and commands
platform_darwin.py macOS paths and commands
platform_windows.py Windows paths and commands
ioc_windows.py Windows-only IOC checks (Registry, Tasks)
discovery.py Phase 1 — find package metadata
version_checker.py Phase 2 — read package version
ioc_scanner.py Phase 3 — IOC artifact detection
source_scanner.py Phase 4 — source/config file scanning
report.py Phase 5 — summary and remediation
tests/ pytest test suite (230 tests)
run_scan.py Direct entry point
run_scan.bat Double-click launcher for Windows
Disclaimer
This tool is provided as-is, with no warranty of any kind. It attempts to find known indicators of supply chain compromises but cannot guarantee complete detection. A clean scan does not mean your system was unaffected. Always perform credential rotation if there is any possibility that a compromised package was installed in your environment, even briefly.
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 scan_litellm_compromise-0.4.0.tar.gz.
File metadata
- Download URL: scan_litellm_compromise-0.4.0.tar.gz
- Upload date:
- Size: 41.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5a3d282114b23bdd815b989d8d446b5c0717536f92de6e8b30444d49121e5f1f
|
|
| MD5 |
bb019b32667e5f3d44af831ef0b6e3cb
|
|
| BLAKE2b-256 |
883805cb61e36c928a8fa1960e2525b77125c6b6c00a773b6feaa1738e40438a
|
Provenance
The following attestation bundles were made for scan_litellm_compromise-0.4.0.tar.gz:
Publisher:
pypi_publish.yml on CognitiveSand/scan_litellm_compromise
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
scan_litellm_compromise-0.4.0.tar.gz -
Subject digest:
5a3d282114b23bdd815b989d8d446b5c0717536f92de6e8b30444d49121e5f1f - Sigstore transparency entry: 1204169488
- Sigstore integration time:
-
Permalink:
CognitiveSand/scan_litellm_compromise@3c0ee825600d90be04da11354141821677b3c15d -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/CognitiveSand
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi_publish.yml@3c0ee825600d90be04da11354141821677b3c15d -
Trigger Event:
release
-
Statement type:
File details
Details for the file scan_litellm_compromise-0.4.0-py3-none-any.whl.
File metadata
- Download URL: scan_litellm_compromise-0.4.0-py3-none-any.whl
- Upload date:
- Size: 37.2 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 |
fab77198045f68f83d3803456cb899bb6510e23b8663d4aa01f9bb463581239d
|
|
| MD5 |
1addf458570ca66005fe17880498061f
|
|
| BLAKE2b-256 |
a4a19e063189dcf7c55c764bf2b1cd75a4e1cd6e49b49ecb2c0c84a52420e9d5
|
Provenance
The following attestation bundles were made for scan_litellm_compromise-0.4.0-py3-none-any.whl:
Publisher:
pypi_publish.yml on CognitiveSand/scan_litellm_compromise
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
scan_litellm_compromise-0.4.0-py3-none-any.whl -
Subject digest:
fab77198045f68f83d3803456cb899bb6510e23b8663d4aa01f9bb463581239d - Sigstore transparency entry: 1204169490
- Sigstore integration time:
-
Permalink:
CognitiveSand/scan_litellm_compromise@3c0ee825600d90be04da11354141821677b3c15d -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/CognitiveSand
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi_publish.yml@3c0ee825600d90be04da11354141821677b3c15d -
Trigger Event:
release
-
Statement type: