TrapDoor: 34 packages went bad overnight, and a 3-day rule would have stopped them
In late May 2026, security researchers disclosed a supply-chain campaign that came to be known as TrapDoor. More than 34 malicious packages went up across PyPI, npm and Crates.io at roughly the same time. They were not broken or buggy. They were built to run hostile code the moment they were installed or imported, scraping SSH keys and credentials off the machine and sending them to the attackers (reported by The Hacker News , 25 May 2026).
If you have ever wondered why the 5bats tools exist, this is the short answer. TrapDoor is the attack the toolset was built in response to, and the uncomfortable part is how ordinary it was. No zero-day, no clever exploit. Just packages, published normally, that a normal install command pulled straight onto developers’ machines.
What actually happened on the machine
Picture the sequence from the developer’s side. You run an install for a package you need, or an update to one you already use. The package manager fetches it and runs its setup. In that setup, on a normal laptop or build server, a few extra lines do their work. They look for the files a developer almost always has: the SSH private key in ~/.ssh, the cloud tokens in environment variables, the credentials cached by other tools. They quietly send copies to a server the attacker controls. No window pops up. The install finishes, reports success, and you move on to the next thing.
The theft has already happened, and nothing on screen tells you so. That is the shape of every package in the TrapDoor set: not a bug waiting to be triggered later, but an action that completes during installation.
What made it slip through
Here is the detail that matters: when researchers caught it, every one of the malicious PyPI packages was less than 72 hours old. That is not a coincidence. It is the whole strategy. A brand-new release lives in a blind spot. The security databases that tools check against have not seen it yet, so there is no CVE, no advisory, nothing to flag. By the time the ecosystem catches up and pulls the package, it has already run on every machine that installed it in those first hours.
That is why the usual advice, “scan your dependencies for known vulnerabilities,” does not help here. There was nothing known to scan for. And post-install scanners run after the package’s code has already executed, which for an attack that fires on install is far too late. The defenders were not slow or careless. They were playing a game where the attacker moves first and the scoreboard updates last.
The second trick: turning your assistant against you
TrapDoor had a second trick worth dwelling on, because it points at where this is heading. Some payloads planted hidden instructions in CLAUDE.md and .cursorrules files, the configuration that steers AI coding assistants. On its own that does nothing. But the next time the developer opened their assistant, it read those files as trusted project instructions and acted on them: fetching more code, leaking more secrets, or quietly weakening a defence, all while looking like it was doing the developer’s bidding.
So TrapDoor was both a supply-chain attack and a prompt-injection one. The first break-in stole credentials. The second turned your own tools into an accomplice on the next run. As more of us let AI assistants install and run things on our behalf, that combination stops being a curiosity and becomes the main event.
What would have stopped it
This is the point of a recap like this. Not to alarm, but to show that the defence is unglamorous and effective.
- A freshness hold. pip-cve-gate refuses, by default, to install any release younger than three days. Against TrapDoor that single rule is decisive: every malicious PyPI package was inside that window, so they would have been blocked outright, no CVE required, nothing to look up. The attack’s greatest strength, its newness, is exactly what a freshness hold turns into a weakness. You wait three days, the crowd finds the bad release, and you never pull it.
- A pre-install check, not a post-install one. Because the gate runs before the install, the hostile code never gets its moment to execute. A scanner that reports a problem after the setup script has run is writing the incident report, not preventing the incident. The same pre-install idea covers Composer and Homebrew , and for the packages an AI assistant installs on your behalf, claude-code-cve-gate applies it there too.
- A line between reading and running. For the
CLAUDE.mdtrick, claude-code-prompt-injection-gate locks the files an attacker would want to rewrite, so a poisoned package cannot quietly reprogram your assistant. Reading those files for context is fine. Letting an unreviewed package edit them is not.
Why this keeps happening
It is worth being honest about why attacks like TrapDoor are easy to run. Public registries are open by design. Anyone can publish, updates are instant and global, and millions of machines are configured to pull the newest version automatically. That openness is what makes the open-source ecosystem productive, and it is also the attack surface. You cannot close it without breaking the thing that makes it useful.
What you can do is change your own default: stop trusting brand-new releases on sight, and check before you run rather than after. TrapDoor did not need a flaw in any of those registries. It just used them the way they are meant to be used, faster than anyone could react.
The takeaway
TrapDoor was not the first attack of its kind and it will not be the last, which is the real lesson. You cannot rely on being warned in time, because the most dangerous packages are the ones nobody has been warned about yet. A standing rule that simply waits a few days, and checks before it installs, does not need the warning. It is the difference between defending against the attacks already known and defending against the next one.
For the full explanation of how these attacks work, see the guide on software supply-chain attacks .
