March 2026: The Month npm Trust Died

March 31, 2026 · @fuzzingbrain · FuzzingBrain

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.

The tally: axios (83M weekly downloads) shipped a RAT. Claude Code (Anthropic's flagship coding agent) leaked its entire source. LiteLLM (95M monthly) exfiltrated credentials from every machine that installed it. All in March 2026.

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

March 31, 2026 · Compromised npm account · Discovered by StepSecurity
83M weekly downloads Cross-platform RAT Self-deleting evidence

The 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:

  1. Fingerprints the OS via os.platform()
  2. Downloads a platform-specific payload from sfrclak.com:8000
  3. Establishes persistence (macOS: LaunchAgent; Windows: registry Run key; Linux: nohup background process)
  4. Begins beaconing system inventory to C2 every 60 seconds
  5. Self-destructs: deletes setup.js, deletes the malicious package.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.

Anti-forensics by design. Every trace was engineered to self-destruct. The version mismatch deception means even post-incident audits using npm list or lock file inspection would show a clean dependency tree.

What Attest catches: 18 findings

CRITICAL supply_chain:phantom_dependency
Suspicious dependency added — exists only for its install hook, never imported in source
CRITICAL supply_chain:known_ioc
sfrclak.com C2 domain, plain-crypto-js, nrwise@proton.me — known axios attack indicators
CRITICAL supply_chain:self_deleting_dropper
fs.unlink(__filename) — script deletes itself after execution
CRITICAL supply_chain:powershell_hidden
powershell -w hidden -ep bypass — hidden execution with bypassed policy
CRITICAL supply_chain:registry_persistence
HKCU\Software\Microsoft\Windows\CurrentVersion\Run — Windows registry persistence
HIGH supply_chain:platform_specific_payload
os.platform() branches to different payloads — cross-platform RAT delivery
HIGH supply_chain:nohup_background_exec
nohup python3 /tmp/ld.py ... > /dev/null 2>&1 & — Linux RAT persistence

+ 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

March 31, 2026 · Packaging misconfiguration · Discovered by Chaofan Shou
512K lines exposed 44 feature flags Second incident

This 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:

For competitors, this is a complete product roadmap delivered on a silver platter.

This was the second time. Anthropic confirmed it was "a release packaging issue caused by human error." The fact that it happened twice suggests a systemic gap in their release validation pipeline — exactly the kind of gap a pre-publish oracle could catch.

What Attest catches: 7 findings

CRITICAL packaging_hygiene:source_map_shipped
Source map file added to package — exposes original source when published
CRITICAL packaging_hygiene:source_map_exclusion_removed
*.map exclusion removed from .npmignore — maps will now be published
CRITICAL packaging_hygiene:source_and_flags_exposed
Source maps + feature flags combined — full IP leak (4 map issues + 2 flag references)
HIGH packaging_hygiene:source_map_in_prod_config
sourceMap: true in production build config
MEDIUM packaging_hygiene:feature_flag_exposed
FEATURE_FLAGS, KAIROS, PROACTIVE references visible in shipped code

Incident 3: LiteLLM — The CI/CD chain

litellm@1.82.7 / litellm@1.82.8

March 24, 2026 · CI/CD pipeline hijack · TeamPCP campaign
95M monthly downloads Credential stealer .pth persistence

We 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:

The package registry is the new attack surface. npm and PyPI are implicit trust boundaries. When you 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:

AttackWas there a PR?How it was published
axiosNoDirect npm publish via stolen token
Claude CodeProbablyNormal release pipeline with bad config
LiteLLMNoHijacked 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)

Packaging hygiene oracle (Claude Code patterns)

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

DateEvent
Mar 24TeamPCP publishes backdoored LiteLLM 1.82.7 and 1.82.8 to PyPI
Mar 25LiteLLM confirms compromise, publishes security advisory
Mar 30, 05:57 UTCAttacker publishes clean plain-crypto-js@4.2.0 to npm (staging)
Mar 30, 23:59 UTCAttacker publishes malicious plain-crypto-js@4.2.1
Mar 31, 00:21 UTCCompromised axios@1.14.1 published
Mar 31, 01:00 UTCCompromised axios@0.30.4 published
Mar 31, morningSecurity researcher Chaofan Shou discovers Claude Code source map leak
Mar 31, afternoonStepSecurity identifies axios compromise, npm removes malicious versions
Mar 31Anthropic confirms Claude Code leak: "release packaging issue caused by human error"

References

Attest is built by FuzzingBrain. We build domain-specific security oracles that verify every code change before it reaches production. fuzzbrain.xyz