Malware that runs the moment you open the project.
This is a supply-chain attack — but not the kind npm audit can see.
Instead of poisoning a package you depend on, the dropper is committed directly into the
repository, buried in ordinary config files and disguised as font assets. It executes when
you start the dev server, or the instant your editor opens the folder. No
npm install required.
snare is a free, open-source command-line tool that watches your machine for the loader and kills it, then scans every Git repository you can reach, cleans out what it finds, and helps you warn your collaborators.
216 developers cloned snare in the two weeks to 14 October 2026. Star it so the next one finds it first.
Four routes in, none of which you'd think to check
The same payload ships four ways. Every one of them fires from a completely ordinary developer action — opening a folder, starting the dev server, or just beginning a session with your coding assistant. That is exactly why this family works.
Opening the folder in your editor
.vscode/tasks.json
A task labelled eslint-check, marked "hide": true and
"reveal": "never" so it never appears in the terminal panel. It runs
node ./public/fonts/fa-solid-400.woff2.
▸ fires on "runOn": "folderOpen"
Starting the dev server
postcss.config.js
The payload is appended to the last line after thousands of spaces. The file reads as four correct lines of config, because everything else is far off the right edge of the viewport.
▸ fires on next dev / next build
Running any npm script
package.json
Every script you had is still there, each one now prefixed:
"dev": "node api.js && next dev". The obfuscated api.js sits
at the repository root. Nothing has to be installed for this to run, so
--ignore-scripts makes no difference — these are not install hooks.
▸ fires on npm run dev / build / start
Opening a session with your AI assistant
.claude/settings.json
A SessionStart hook runs node .vscode/setup.mjs every
time a session opens. It survives removing the package that delivered it, npm never sees it,
and the two copies in .claude/ and .vscode/ re-create each other —
so deleting one directory fixes nothing.
▸ fires on every new agent session
“Is this the npm supply-chain thing?”
Same category, different delivery — and the difference is why people miss it.
A classic npm supply-chain attack compromises a package you depend on. It arrives through
npm install, it shows up in your lockfile, and npm audit or a
dependency scanner has a chance of catching it.
This one never touches the registry. The payload is committed into your own
repository, so there is no malicious dependency to find — npm audit is clean,
your lockfile is clean, and Dependabot has nothing to report. The malicious code is sitting in
a file your build already loads. Interestingly, the loader carries an internal vector tag
reading NPM, so the same operator appears to run registry-delivered variants as
well.
If you arrived here searching for the npm attack: run the four checks anyway. They take a minute and they cover the route a dependency scanner cannot see.
The font is not a font
A genuine .woff2 file begins with the magic bytes wOF2. This one
begins with 507 spaces, then JavaScript. Disguising a payload as a binary asset keeps it out of
code review entirely — nobody opens a font in a diff.
The file looks completely normal
Here is the actual shape of an infected postcss.config.js. Four
ordinary lines, then a gap wide enough to push the payload past any screen. Reveal the whitespace
to see what your editor was not showing you.
Line 5 is 9,135 characters long. Scroll it sideways — or reveal the whitespace.
The address it phones home to is not in the code
This is why blocking an IP achieves nothing. The loader ships with no server address at all — it derives a fresh one every run by reading the public Ethereum blockchain, a documented technique known as EtherHiding. The operator moves their infrastructure by sending a single transaction.
Step by step
- Query public Ethereum RPCsFalls through 1rpc.io, drpc.org, publicnode and blastapi, so cutting off any one endpoint changes nothing.
- Read the wallet's newest transactionThrough the Blockscout API, sorted newest first.
- Decode an IP from the destination addressThe first eight bytes are read as two IPv4 addresses — a primary and a fallback.
- Fetch stage two over plain HTTPFrom
/0x/clb,/0x/clsor/0x/ls, on port 443 to look like TLS to a port-based filter. - Decode and spawn it detachedXOR-decoded and evaluated in memory — nothing is written to disk — then
spawn(…, {detached:true})andunref(), so it outlives the editor that started it.
Address → endpoints
The payload observed in the wild stole clipboard contents — passwords, access tokens and wallet seed phrases — from the moment the editor opened.
This is not a one-off
The accounts below are what developers have described first-hand — an internship cohort of several thousand, engineers nearly dismissed over a compromise that was not their doing, and disrupted operations at several companies. These are reports, not audited figures. They are collected in the open precisely so you can read them and judge for yourself. One sample sat in a repository for five months before anyone noticed.
If it reached your team, say so
You do not have to name an employer or client — "a fintech I contract for" is a perfectly good data point. Redact tokens, keys and internal hostnames first. The reason to post is simple: the developers who lost work to this mostly had never heard of the technique, and the ones who caught it early had heard about it from someone else.
Two minutes decides whether this is your problem
Four commands against any clone. No install, no tools, about a minute.
Step 2 Install snareDoes all four checks continuously, across every repository you can reach.
Step 3 ⭐ Star it on GitHubIf snare caught something, or just made you look, a star is how the next developer finds it.