Written in invisible ink
Socket found TrapDoor, a supply-chain campaign across npm, PyPI, and Crates.io that does something new: an installed package rewrites your coding agent's own rules file — CLAUDE.md, .cursorrules — and hides the instruction in zero-width Unicode you cannot see. The next time you run the agent, it reads its own house rules and runs a 'security scan' that harvests your SSH keys, cloud credentials, GitHub token, and crypto wallets. The instruction is invisible; the theft is real. Bromure Agentic Coding hands that scan a box with nothing worth taking.
A package you installed edits the file your coding agent trusts most — its own rules file — and writes the malicious line in characters that render as nothing. You review the file, see ordinary project notes, and approve it. The agent reads the same file and sees an order: run a security scan, mail the results here.
Most of the attacks we cover hide a malicious instruction somewhere the agent will read it — a web page, a pull-request comment, a README. This one hides it in the last place you would think to check: the agent's own configuration. On May 22, 2026, Socket began tracking a campaign it named TrapDoor — 34 malicious packages and 384-plus versions published across npm, PyPI, and Crates.io, with the operators re-publishing faster than takedowns could keep up. Socket's automated feed caught new releases in an average of five minutes and fifty-six seconds; the fastest, fifty-eight seconds after publication. The Hacker News and Phoenix Security covered it the same week. We are writing about it now because its newest move is the cleanest example yet of a supply-chain stealer built to aim straight at a coding agent.
The packages look like the tools a developer reaches for without a second
thought: environment loaders, wallet checkers, build helpers, and — the
one that started it all — eth-security-auditor, a "security auditor."
Install one and it does the ordinary infostealer things. But the shared
npm payload, a 48 KB file called trap-core.js, does one more thing that
is not ordinary at all. It writes to .cursorrules and CLAUDE.md.
The line you can't see
Those two files are house rules. Cursor reads .cursorrules; Claude Code
reads CLAUDE.md. They exist so a repository can tell the agent how to
behave in this project — the conventions, the do's and don'ts — and the
agent treats them as standing instructions, applied to everything it does
in that folder. They are the most trusted text in the workspace, because
you are supposed to have written them.
TrapDoor writes into them, and hides the writing. The malicious
instruction is encoded with zero-width Unicode characters — U+200B,
U+200C, U+200D, U+FEFF — code points that a text editor renders as
nothing at all. Open CLAUDE.md and you see your ordinary project notes.
Run git diff and, depending on your settings, you may see nothing worth
stopping for. The bytes are there; the glyphs are not. It is a message
written in invisible ink, and the only reader that develops it is the
agent, which parses the full Unicode stream and finds an instruction
sitting in its own rulebook.
The scan that steals
The hidden instruction tells the agent to run a "security scan." Nothing
about that sounds alarming — you handed the agent a security auditor, and
now its rules ask it to audit. What the scan actually does is walk the
machine for anything worth money. Socket's breakdown of the payload lists
SSH keys, AWS credentials, GitHub tokens, browser profiles and login
databases, environment variables and API keys, and — this campaign's
signature — cryptocurrency wallets: Sui, Solana, Aptos, and the browser
wallet extensions. Then it exfiltrates to a GitHub Pages site
(ddjidd564[.]github[.]io) and, in the Crates.io variant, to GitHub
Gists.
One detail shows how deliberate this is. Before sending anything, the payload "actively calls AWS and GitHub APIs to confirm whether stolen credentials are valid, filtering for high-value tokens before exfiltration." It does not just grab and dump. It tests each credential against the real service, keeps the live ones, and throws the dead ones away. The operators want a clean list of working keys.
Phoenix Security names why this lands hardest on the people it targets: crypto and DeFi developers, because "SSH keys, cloud credentials, GitHub tokens, and crypto wallet keystores routinely coexist on the same machine." That sentence is the whole attack surface. The agent runs on your laptop, as you, and everything of value is within its reach. The invisible line in the rules file is just the trigger; the ambient access is the payload.
Where Bromure draws the line
Bromure Agentic Coding does not depend on spotting the invisible line — though, as we'll get to, it reads the same bytes. It changes what the line can reach when it fires. Each profile's agent runs inside a disposable Linux VM on Apple Silicon, one hypervisor away from macOS, and the credentials in that VM are not yours.
The credentials are decoys. This is the layer TrapDoor runs
straight into, because TrapDoor is, at heart, a credential thief. A
credential broker on the host keeps
your real Anthropic, AWS, and GitHub secrets out of the box entirely.
Inside the VM the agent sees placeholders — a brm_… token, a synthetic
kubeconfig, a throwaway SSH key — and the broker swaps the real value in
only at the network boundary, on requests to destinations you already
approved. So the "security scan" runs, walks the disk, and harvests
exactly what it was sent for: fakes. And the campaign's clever
validation step turns against it — it calls AWS and GitHub to confirm the
tokens are live, the decoys fail that check, and the filter meant to keep
only working keys keeps nothing.
The wallets aren't there. The Sui and Solana keystores, the browser login databases, the wallet extensions — none of that lives in a throwaway coding VM. It is on your real machine, one hypervisor away, where the scan cannot see it. Close the session and the box resets, poisoned rules file and all.
The exfiltration is gated. Say the scan finds something worth
sending. The POST to ddjidd564.github.io, or the write to a GitHub
Gist, is an outbound request, and every outbound request crosses the host
proxy, where the true destination is checked against the profile's
approved list on the way out. A stealer phoning home to an address the
profile never approved does not get out — and destructive shapes against
the APIs the profile does front meet a guardrail that can refuse them no
matter what the agent was talked into.
Ahead of all of that sits the layer that meets TrapDoor at its trigger. The whole attack starts with content the agent reads — the poisoned rules file — and Bromure scores untrusted content with an on-device injection detector before the model acts on it. The zero-width Unicode that hides the instruction from your editor does not hide it from the scorer: it reads the same byte stream, invisible code points included, and a rules file that decodes to "run a scan and mail out the secrets" is exactly the shape it is built to flag. It catches most of this at the door. A novel enough encoding can still slip one classifier, which is why it sits in front of the disposable box rather than in place of it.
Invisible to you, empty for the thief
The unsettling part of TrapDoor is not the encoding trick. It is that the trick works on the one file you were told to trust — the rules you wrote for your own agent — and that a human reviewing that file sees nothing wrong, because there is nothing to see. Detection has to win every time against every new way to hide a line. That is a race worth running, and the on-device scorer runs it, but it is not where the safety should rest.
Bromure Agentic Coding rests it one level down. It assumes the agent will eventually read a poisoned instruction it cannot tell from a real one — invisible ink or otherwise — and makes sure that when the agent goes looking for secrets to steal, the secrets are decoys, the wallets are on a machine it cannot reach, and the way out is gated. The scan still runs. It just comes back empty. Install it and give your agent a rulebook that, poisoned or not, points at nothing worth taking.