March 2026: The Month npm Trust Died
In the span of eight days, three of the most widely used JavaScript and Python packages were compromised or exposed. Not through zero-day exploits. Not through AI-generated code. Through the most mundane failures imaginable: a stolen npm token, a forgotten .npmignore rule, and a hijacked CI pipeline.
Together, these incidents affected packages downloaded over 270 million times per month.
See Attest detect all three attacks
Live, interactive demos — run the oracle pipeline against each incident. No signup.
Run Live Demos →Incident 1: axios — The phantom dependency
axios@1.14.1 / axios@0.30.4
83M weekly downloads Cross-platform RAT Self-deleting evidenceThe attacker compromised the npm account of jasonsaayman (an axios maintainer), changed its email to a Proton Mail address under their control, and used a long-lived classic npm access token to publish two poisoned versions.
The attack was surgically precise. Both release branches were hit within 39 minutes. The only change to axios itself was a single line in package.json:
"dependencies": {
"follow-redirects": "^2.1.0",
"form-data": "^4.0.1",
"proxy-from-env": "^2.1.0",
+ "plain-crypto-js": "^4.2.1" // <-- never imported anywhere in axios
}That's it. One phantom dependency. plain-crypto-js was never imported, never used, never referenced in any source file. Its sole purpose was its postinstall hook.
The dropper: setup.js
The 4KB dropper used two-layer obfuscation: reversed base64 with underscore substitution, followed by XOR cipher with key OrDeR_7077 and index calculation 7*i*i % 10 + 333. This protected 18 encoded strings containing module names, C2 URLs, and shell commands.
Within 15 seconds of npm install, the dropper:
- Fingerprints the OS via
os.platform() - Downloads a platform-specific payload from
sfrclak.com:8000 - Establishes persistence (macOS: LaunchAgent; Windows: registry Run key; Linux: nohup background process)
- Begins beaconing system inventory to C2 every 60 seconds
- Self-destructs: deletes
setup.js, deletes the maliciouspackage.json, renames a pre-staged clean stub (package.md) into its place
After infection, npm list shows plain-crypto-js@4.2.0 (the clean version number from the stub), not 4.2.1. The postinstall hook is gone. The dropper is gone. Only the RAT remains.
npm list or lock file inspection would show a clean dependency tree.
What Attest catches: 18 findings
+ 11 more findings (additional IoC matches, fake User-Agent, packaging warnings)
Incident 2: Claude Code — The accidental leak
@anthropic-ai/claude-code@2.1.88
512K lines exposed 44 feature flags Second incidentThis one wasn't malicious. It was worse — it was mundane.
Anthropic published @anthropic-ai/claude-code version 2.1.88 to npm with a .map file referencing the full, unminified TypeScript source. The source map pointed to an R2 bucket containing a downloadable ZIP of the entire src/ directory: 1,900 files, 512,000 lines of code.
The root cause was a three-part packaging failure:
// 1. tsconfig.json — source maps enabled for production
- "sourceMap": false,
+ "sourceMap": true,
// 2. .npmignore — *.map exclusion removed
src/
test/
.github/
- *.map <-- this line was deleted
// 3. package.json — .map files explicitly included
"files": [
"dist",
"README.md",
"LICENSE",
+ "dist/*.map"
]Any one of these alone might not have caused a leak. Together, they exposed everything.
What was exposed
The leaked source revealed Anthropic's complete architecture for Claude Code: a 512K-line TypeScript codebase running on Bun with React+Ink terminal UI, ~40 permission-gated tools, a 46K-line query engine, multi-agent "swarm" orchestration, and bidirectional IDE integration via JWT auth.
More damaging: 44 compile-time feature flags for unreleased capabilities:
- KAIROS — an autonomous daemon mode where Claude Code runs as an always-on background agent, performing "memory consolidation" during idle periods
- ULTRAPLAN — 30-minute remote planning sessions with deep codebase analysis
- VOICE_MODE — voice interaction
- BUDDY_COMPANION, COORDINATOR, WORKFLOW_SCRIPTS, BRIDGE_MODE
For competitors, this is a complete product roadmap delivered on a silver platter.
What Attest catches: 7 findings
Incident 3: LiteLLM — The CI/CD chain
litellm@1.82.7 / litellm@1.82.8
95M monthly downloads Credential stealer .pth persistenceWe covered this in detail in our LiteLLM case study. The short version: TeamPCP compromised the Trivy GitHub Action, used that access to steal LiteLLM's PyPI publishing token, then published two backdoored versions that harvested SSH keys, AWS credentials, and K8s configs from every installer.
The .pth file persistence mechanism was particularly insidious — a single-line file that auto-executes the credential stealer on every Python invocation, not just when LiteLLM is imported.
Attest catches 8 findings including obfuscated exec, credential harvesting, .pth persistence, and aggregate attack pattern detection. Read the full analysis →
The common thread
These three incidents look different on the surface — a RAT, a source leak, a credential stealer. But they share a root cause:
npm install or pip install, you're executing code from whoever last published to the registry. No PR review. No CI check. No human in the loop.
Traditional security tools are designed for code that goes through a pull request. But supply chain attacks bypass PRs entirely:
| Attack | Was there a PR? | How it was published |
|---|---|---|
| axios | No | Direct npm publish via stolen token |
| Claude Code | Probably | Normal release pipeline with bad config |
| LiteLLM | No | Hijacked CI/CD published directly to PyPI |
Two of three never touched GitHub. The tools watching GitHub saw nothing.
What we built
After the LiteLLM incident, we built Attest's supply chain oracle — a deterministic pattern matcher that checks any code diff against techniques from real attacks. After the Claude Code leak, we added the packaging hygiene oracle that catches accidental exposure through build misconfigurations.
These aren't AI-based classifiers that might hallucinate. They're rule engines with 30+ rules derived from forensic analysis of actual incidents:
Supply chain oracle (axios, LiteLLM patterns)
- Obfuscated execution (base64+exec, XOR encoding, nested decoding)
- Credential harvesting (SSH keys, cloud creds, env dumps)
- RAT dropper patterns (platform detection, C2 beaconing, self-deletion)
- Persistence mechanisms (registry keys, systemd, .pth files, nohup)
- Phantom dependencies (added but never imported)
- Known IoCs (C2 domains, attacker identifiers)
- Combined signal aggregation (obfuscation + credentials = critical)
Packaging hygiene oracle (Claude Code patterns)
- Source map files in published packages
- Source map exclusion removed from .npmignore
- Source maps enabled in production build config
- Feature flags visible in shipped code
- Hardcoded credentials in publishable files
- Missing package scoping (no
filesfield or .npmignore)
Try it yourself
Three live demos, three real attacks
Run Attest's oracle pipeline against simulated diffs from all three incidents. No signup required.
Run Live Demos →Or scan your own repo: fuzzbrain.xyz/app
What's next
March 2026 proved that supply chain attacks are not edge cases. They're the main vector. The npm and PyPI registries serve billions of downloads per week to machines that execute whatever they receive, no questions asked.
We think every npm publish and pip upload should run through an oracle pipeline before it reaches the registry. Not just malicious payload detection — packaging hygiene, credential scanning, feature flag exposure, the full surface area.
If you maintain an open-source package or run a security team, try Attest on your repo. Connect in 60 seconds, no code changes required.
Timeline
| Date | Event |
|---|---|
| Mar 24 | TeamPCP publishes backdoored LiteLLM 1.82.7 and 1.82.8 to PyPI |
| Mar 25 | LiteLLM confirms compromise, publishes security advisory |
| Mar 30, 05:57 UTC | Attacker publishes clean plain-crypto-js@4.2.0 to npm (staging) |
| Mar 30, 23:59 UTC | Attacker publishes malicious plain-crypto-js@4.2.1 |
| Mar 31, 00:21 UTC | Compromised axios@1.14.1 published |
| Mar 31, 01:00 UTC | Compromised axios@0.30.4 published |
| Mar 31, morning | Security researcher Chaofan Shou discovers Claude Code source map leak |
| Mar 31, afternoon | StepSecurity identifies axios compromise, npm removes malicious versions |
| Mar 31 | Anthropic confirms Claude Code leak: "release packaging issue caused by human error" |
References
- StepSecurity: axios compromised on npm
- Socket.dev: axios npm package compromised
- axios GitHub issue #10604
- VentureBeat: Claude Code source code leak
- LiteLLM: security update
- Attest: Detecting the LiteLLM attack (full case study)
- Malwarebytes: axios supply chain attack
Attest is built by FuzzingBrain. We build domain-specific security oracles that verify every code change before it reaches production. fuzzbrain.xyz