TrapDoor Hit npm, PyPI, and Crates.io at Once. Then It Poisoned Your AI Assistant.
34 malicious packages across three ecosystems. Every one scored 15 or lower. The new part: zero-width Unicode instructions hidden in .cursorrules and CLAUDE.md, designed to turn your coding assistant into an exfiltration tool.
On May 22, 2026, starting at 20:20 UTC, a cluster of accounts began publishing packages across npm, PyPI, and Crates.io in rapid waves. 34 packages, 384 versions. The campaign was named TrapDoor.
What made it different wasn’t the credential theft—that’s table stakes now. It was the secondary payload: poisoned .cursorrules and CLAUDE.md files containing invisible instructions, embedded using zero-width Unicode characters.
The instructions told AI coding assistants to run a “security scan” that harvested local secrets and exfiltrated them.
Three ecosystems, one campaign
Previous supply chain attacks stayed within one registry. TrapDoor hit all three simultaneously, using each ecosystem’s native execution mechanism:
- npm (21 packages):
postinstallhooks. Immediate execution onnpm install. - PyPI (7 packages): Auto-execute on import. The payload downloads a remote script.
- Crates.io (6 packages):
build.rsscripts. Run at compile time, before your code executes.
The package names read like a developer’s wishlist: llm-context-compressor, prompt-engineering-toolkit, wallet-security-checker, defi-risk-scanner. Each one targets a specific community—AI developers, crypto builders, DevOps engineers—with names designed to appear in the right search results.
What the behavioral scores say
I ran every TrapDoor package through Commit’s behavioral audit. The results are uniform:
| Package | Ecosystem | Score | Publishers | Downloads | Age |
|---|---|---|---|---|---|
llm-context-compressor | npm | 15 | ? | 42/wk | <1 yr |
prompt-engineering-toolkit | npm | 15 | ? | 35/wk | <1 yr |
model-switch-router | npm | 15 | ? | 42/wk | <1 yr |
workspace-config-loader | npm | 15 | ? | 21/wk | <1 yr |
cryptowallet-safety | PyPI | null | ? | 0/wk | <1 yr |
defi-risk-scanner | PyPI | null | ? | 0/wk | <1 yr |
Every single package: score 15 or null. Zero behavioral history. No longevity. No community. No release track record. No linked GitHub repository.
A behavioral gate set to block HIGH-risk packages would have stopped all 34 on first install. No CVE database needed. No signature matching. No waiting for a report.
The AI poisoning vector
This is the part that should change how you think about supply chain attacks.
TrapDoor didn’t just steal credentials directly. It planted .cursorrules and CLAUDE.md files containing zero-width Unicode characters. The files look blank or innocuous in a text editor. But AI coding assistants read every character.
The hidden instructions directed the assistant to run a “security scan”—a plausible-sounding task that the AI would execute without suspicion. The scan harvested SSH keys, git credentials, VS Code settings, source headers, and environment variables. Exfiltration went through GitHub Pages and raw.githubusercontent.com—domains your firewall almost certainly allows.
This is a different threat model. Traditional malware runs when you install a package. AI poisoning runs when your assistant reads a file. The attack surface isn’t postinstall hooks—it’s the context window.
Why this matters for AI-assisted development
When a human developer installs llm-context-compressor, they might check the README, scan the GitHub repo, read a few issues. When Cursor or Claude Code installs it, the decision is made in milliseconds based on the package name matching the prompt.
TrapDoor’s package names were engineered for exactly this scenario. prompt-engineering-toolkit is what an AI assistant would suggest when you ask for prompt management. workspace-config-loader is what it would suggest when you need config loading. The names are the attack.
And once the package is installed, the .cursorrules or CLAUDE.md file provides a second payload delivery mechanism that doesn’t require any code execution at all—just an AI assistant reading its own configuration.
What to do about it
Gate your AI’s installs. One command adds a behavioral gate to both Cursor and Claude Code:
npx proof-of-commitment hook
This intercepts every npm install, pip install, cargo add, and go get that your AI assistant runs. Packages with a score below the threshold get blocked before they touch your machine. Every TrapDoor package would have been caught.
For stricter protection (recommended if you work with crypto or AI infra):
COMMIT_HOOK_SEVERITY_BLOCK=HIGH npx proof-of-commitment hook Audit what you already have:
npx proof-of-commitment --file package-lock.json Check for poisoned config files. Search your project for zero-width characters in .cursorrules, CLAUDE.md, and any .mdc files:
# Find zero-width Unicode in config files
grep -rP '[\x200B\x200C\x200D\xFEFF\x2060]' .cursorrules CLAUDE.md **/*.mdc 2>/dev/null If that grep returns matches, you have a problem.
The pattern keeps repeating
LiteLLM in March. axios in late March. Shai-Hulud in May. Red Hat Miasma in June. Now TrapDoor.
Each attack escalates. LiteLLM was one package in one registry. Shai-Hulud was a self-propagating worm. Miasma forged provenance attestations. TrapDoor crossed three registries and weaponized AI assistants.
The entry point changes. The underlying pattern doesn’t: packages with no behavioral history get published, and nothing gates the install.