Keyv and friends compromised in active Shai-Hulud supply chain attack (aikido.dev)

198 points by cimi_ 9 hours ago

ChrisMarshallNY 3 hours ago

OW. That's gonna leave a mark.

It sucks that we have this glass-jaw dependency system, which is really the main reason these supply chain attacks work.

Really hard to clean up, too. These days, you (being the blackhat) would likely send agents to leverage every compromised repo/app/Web site, almost the instant it comes online, so even if the original mess is cleaned up, there's still a ton of knock-on compromises.

xnorswap 6 hours ago

At this point, any package adding a pre-install hook where there previously was not one should be denied and treated with extreme suspicion.

It's time pre-install / post-install hooks were killed off. Start with a moratorium on any new ones.

jitl 6 hours ago

yeah but they can just put the dropper, etc in index.js, so that it runs at import time rather than at install time, no? i guess first-install time is often a privileged developer machine, and will execute in a "server"-like runtime such as Node, Bun, Deno. but blocking preinstall scripts is basic first aid...

jerf 6 hours ago

Nobody is claiming this is a complete solution to security. I would call this "necessary but not sufficient". You won't get far as an engineer if you refuse to implement "necessary but not sufficient" changes because the change doesn't in and of itself one-shot the entire problem.

csomar 5 hours ago

rcxdude 6 hours ago

insanitybit 5 hours ago

Prod tends to have less privileges than CI/CD. CI/CD tends to be full admin, so it's far more sensitive. Prod tends to have tooling for detecting breaches, better logging, etc. People tend to use containers, which act as a sandbox.

Prod also won't be wormable the way that CI/CD is. With CI/CD I can own another dev, use their creds to push another malicious build script, etc. "Attacker is in my prod env" isn't wormable.

Yes, capabilities in prod would be hugely beneficial but removing CI/CD is massive as a win.

JustSkyfall 5 hours ago

sysguest 5 hours ago

well Deno has the necessary ingredients for defense: file-system permission by path

mechazawa 6 hours ago

iirc does pnpm not allow them by default. But even if we killed them off there would still be a chance of the malware hooking into something else or only working in cli applications.

madeofpalk 3 hours ago

The latest version of all node package managers (npm, yarn, pnpm) now deny this by default. pnpm was ahead of the curve.

jonchurch_ 3 hours ago

npm v12 released last month also defaults into blocking them by default

insanitybit 6 hours ago

You'll just end up with people running `./configure` scripts or whatever instead. The solution I've currently landed on is:

1. Audit build scripts/ proc macros for rust code (and mark with cargo-vet).

2. Have an isolated workflow for "build/test/push artifact to temporary place" (s3, github artifact, whatever). No API keys in this workflow.

3. Have another workflow that has the API keys to publish that grabs the artifact and then places it into a registry.

This creates clear separation of "code runs here" and "environment has privileges".

In my own slop-driven programming language I have build scripts declare their capabilities upfront so that you can statically reason about them (same with runtime permissions).

freakynit 2 hours ago

Updated my docs covering these attacks since 2025:

1. NPM Supply Chain Attack Techniques: https://npm-supply-chain-attack-techniques.pagey.site/

2. NPM Ecosystem Threat Report: https://npm-supply-chain-attacks-25-26.pagey.site/

jesse_dot_id 3 hours ago

echo "min-release-age=5" >> ~/.npmrc

This should be your default minimum if you work with node.

mittermayr 6 hours ago

Does anyone happen to have a grep or similar that helps me check if this is showing up anywhere in the trillions of files in node_modules (or pnpm store)?

chime 3 hours ago

Highly recommend `fd` for the sheer speed:

    fd -HI "^(setup\.mjs|Math_Symbol\.js|math_init\.js)$"
1. https://github.com/sharkdp/fd

2. `brew install fd`

rhdunn 6 hours ago

The what happened section mentions the addition of the `setup.mjs` and `Math_Symbol.js`, along with a change in `package.json` to add `"preinstall": "node setup.mjs"`, so presumably checking for any of those would be a good indication to check further.

For example:

    find . -type f | grep -P "/Math_Symbol\.js$"

yread 5 hours ago

Crucially, Math_Symbol.js that is almost 800KB, not the innocent 1KB one from regenerate-unicode-properties

somebudyelse 3 hours ago

byutifu 5 hours ago

This article has a lot of information and ways to check & clean: https://safedep.io/keyv-npm-supply-chain-compromise/

somebudyelse 3 hours ago

I ended up asking my agent with auto mode:

can you search all installed node modules for any sign of the shai hulud supply chain attack? What happened Every package in the family received two new files, setup.mjs and Math_Symbol.js, along with a "preinstall": "node setup.mjs" entry added to each package.json. Anyone who ran npm install against an affected version would have had setup.mjs execute automatically before their install completed.

setup.mjs is a heavily obfuscated dropper. Its only job is to silently download the Bun JavaScript runtime from github[.]com/oven-sh/bun/releases/download/bun-v1.3.13/ and use it to execute the real payload, Math_Symbol.js:

execFileSync(<bun binary>, ['<script_dir>/Math_Symbol.js'], { stdio: 'inherit', cwd: <script_dir> }) The Math_Symbol.js is a heavily obfuscated 728 KB JavaScript file containing credential stealers that harvest secrets from the victim's environment, encrypt the findings, and exfiltrate them to a public GitHub repository whose description reads "Shai-Hulud: Here We Go Again". The payload also contains worm-like propagation functionality to infect packages of other maintainers that have installed one of the compromised packages.

orheep 6 hours ago

find . -type d -name node_modules -prune -exec find {} \( -name setup.mjs -o -name math_init.js -o -name Math_Symbol.js \) \; 2>/dev/null

evertheylen 2 hours ago

Once again, I ask myself: should we start "shaming" developers who don't use isolation? It still seems I am the exception and most people run their dev environment with full permissions. Why?

I also wrote an article (https://evertheylen.eu/p/shame-devs-without-isolation/) to flesh out my thoughts, but I'd be really happy to discuss this in the comments.

avaer 7 hours ago

I am kind of surprised GitHub doesn't seem to have built a simple classifier for public repos to proactively lock the account of anyone uploading such obviously fishy things (for their own good, at least before the repo is publicly findable), so it can't be used as a rendezvous.

Surely Github's software is good enough that an intern can slop the 80/20 together in a day? It would be an actually good use of AI spending.

abhisek 6 hours ago

twistedpair 18 minutes ago

Ah, I see your problem there

> This requirement will be progressively enforced over time.

drakythe 2 hours ago

"A while back" -- One week ago.

I am curious if they are still implementing the process or if this particular attacker already figured out a way around it.

hulitu 6 hours ago

> I am kind of surprised GitHub doesn't seem to have built a simple classifier for public repos to proactively lock the account of anyone uploading such obviously fishy things (for their own good, at least before the repo is publicly findable), so it can't be used as a rendezvous.

Maybe that's the idea. Regards, the <insert your favourite 3 letter agency here>

tomjen3 an hour ago

Where is the fbi in this? Why has no one been arrested? This is a massive crime.

ethanwillis 5 hours ago

You know with all this AGI swirling around nowadays that is stronger than nation state hackers you think one of these companies would demonstrate just how capable they are by defending public infrastructure.

Unless...

Maybe in 6 months.

pixl97 3 hours ago

LLMs are better at attacking than writing secure code.

LLMs aren't terrible at securing systems, humans are bad at using secure systems and typically disable measures with insecure workarounds.

codeduck 5 hours ago

Oh boy, it's a big one.

TacticalCoder 5 hours ago

> Oh boy, it's a big one.

Yup the "Update" in TFA is scary:

"Update — August 4, 2026, 13:37 CEST: At least 434 packages (across 1381 versions) have been compromised by the worm, with a combined total of over 2 billion monthly installs at the time of writing."

Lots of pain ahead.

twistedpair 16 minutes ago

2,523 and counting

somebudyelse 5 hours ago

the irony of the update being at 1337

thinkingemoji 5 hours ago

No way to prevent this says only package manager where this regularly happens

insanitybit 4 hours ago

This is the most boring comment posted on every one of these. NPM is by no means the worst offender here and offers a ton of solutions to this, lots of package managers are behind it or equivalent.

NPM gets targeted a lot because it's popular. That's it.

acdha 3 hours ago

It’s correct that NPM is not unique but it is the worst for cultural reasons: no other ecosystem started with such a limited language, which lead to the culture of publishing tons of small packages working around things which everything else had builtin. A Python project which has a hundred dependencies is considered quite large but the median React project had north of 30 thousand for years and years.

insanitybit 3 hours ago

altcognito 4 hours ago

> NPM is by no means the worst offender here

Ok, I can agree it is a boring comment, but who is worse?

NPM gets targeted both because it is popular and because there is a wider attack surface (lots of little packages promoted by a huge variety of users) I have a high schooler who published work a couple weeks ago. This is good, but it comes with downsides. Maybe a couple more speed bumps or classifiers would be helpful. Maybe a consolidation of under maintained projects and deprecation is in order.

insanitybit 4 hours ago

walrus01 4 hours ago

Other than what happened with 'xz', which was upstream of it getting packaged, how many times has this happened in the debian packages system? Also very popular.

insanitybit 4 hours ago

lurkerforawhile 2 hours ago

left-pad was over a decade ago. it's a problem with the registry itself, more than just the package manager.

insanitybit 22 minutes ago

rvz 4 hours ago

No other package manager is worse than NPM. Outside of its 'popularity', there are several fundamental reasons why this continues to happen to NPM:

- Imported packages are not pinned by default.

- Typescript / Javascript's lack of a standard library encourages the developer to import more packages into their codebase to address the short-comings which increases the risk of importing a bad package.

- Post install scripts execute external code by default upon downloading dependencies.

All of this comes by default in the ecosystem and we continue to see more shai-hulud worms all easily targeting NPM. Not even signed packages are enforced by default either.

fr3dx 3 hours ago

madeofpalk 3 hours ago

insanitybit 4 hours ago

jesse_dot_id 3 hours ago

echo "min-release-age=5" >> ~/.npmrc

Catloafdev 5 hours ago

I mean what are these devs doing that they are day-1 updating npm packages still?

How many instances of this are required before npm package maintainers learn?

nubg 2 hours ago

ad slop post

varunsharma07 3 hours ago

We (StepSecurity) published a full analysis of both payload stages: https://www.stepsecurity.io/blog/chaindrop-npm-worm

Some additional detail from our analysis:

1. Provenance did not fail, it worked as designed and still shipped malware. The initial 11 packages were published through npm OIDC Trusted Publishing with valid SLSA attestations. The attacker compromised the maintainer's GitHub account and let the projects' own release workflows publish. Provenance proves which commit was built, not that the commit was authorized.

2. There is a booby trap on remediation: the worm installs a watcher that fires an attacker payload when the stolen GitHub token gets revoked. Remove the token monitor first, then rotate.

3. Persistence goes beyond node_modules. It writes .claude/settings.json (SessionStart hook) and .vscode/tasks.json (runOn: folderOpen), each re-executing the dropper. Check repo dotfiles too.

4. No C2 domain to sinkhole: exfil endpoints resolve at runtime from an Ethereum contract. Observed domain is npm-cache.com, but the operator can rotate it and push new code to infected hosts.

If you're auditing: look for setup.mjs, a 727,680 byte Math_Symbol.js (math_init.js in the second wave), and "preinstall": "node setup.mjs" in package.json. Careful, regenerate-unicode-properties ships a legitimate 1 KB Math_Symbol.js; the malicious one is over 700 KB.

Full IOC and package list in the post, updated as the campaign develops.

nubg 2 hours ago

ai slop comment. post the original prompt

daniel_mcsoft 4 hours ago

The cheapest mitigation almost nobody deploys: a version cooldown. These worms get caught fast — this one was flagged same-day, and the article's own timeline shows detection racing ahead of spread. If your CI simply refuses to adopt any version published in the last N days (Renovate supports this natively via minimumReleaseAge), you convert "worm spreads through the ecosystem in hours" into "worm must survive N days of public scrutiny before it can reach you." You give up almost nothing: how often does your product genuinely need a dependency version that's 48 hours old?

Combine that with the workflow split insanitybit describes — build/test jobs holding zero publish credentials, a separate publish job that only touches a finished artifact — and the wormable path is mostly closed without waiting for npm to redesign itself.

None of this is "sufficient" in rcxdude's sense, and that's fine. Sufficiency isn't the bar during an active outbreak; raising the attacker's cost per hop is.

freakynit 2 hours ago

First point.. plus, OTP/MFA authenticated publishing.

This OTP/MFA should come from package repositories, before the package is made publicly available. This is needed so that CD stage is not blocked.

OTP/MFA should be scoped to publishing user/org, not the package. How the OTP/MFA client is managed across the maintainers/org, lies in the scope of maintainers/org.

pixl97 an hour ago

This still does nothing to prevent anyone from publishing a bad package, for example a compromised device/dev in the organization.

A cooldown of a day, and maybe not updating on weekends will save you from that.

It's time to stop moving at the speed of stupid.

cute_boi 6 hours ago

I think npm can use chatgpt/claude for each published package to detect these types of attack? And if it sees they can restrict the package from making any changes.

wolfi1 5 hours ago

the remedy is worse than the disease

ftlps 6 hours ago

aikido.dev: SAST, AI code analysis and therefore a website that uses 100% CPU and prevents scrolling.

To the point of the article: I don't know why GitHub still allows the release feature. It is complete insanity. Tar archives must be constructed manually and checked for leaked keys etc.

tgv 6 hours ago

bijowo1676 3 hours ago

I have a suspicion that a lot of these supply chain compromises are done by the security researchers at security vendors, selling software to protect the software supply chain. Spreading fear to create demand for their products.

Like in the good ole days of Windows 98 and antivirus era, a lot of advanced virus techniques in the wild came from the people who used to work for AV companies

woodruffw 3 hours ago

That would be extremely surprising, given the number and severity of federal crimes involved.

(I also dare say: many of these attackers demonstrate a better in-depth understanding of packaging ecosystems than supply chain security vendors do.)

bijowo1676 2 hours ago

the federal crimes part is irrelevant if its below the threshold of federal authorities actually cracking down on them and figuring out entire chain.

just because credit card theft and other types of scam are illegal

I remember how ddos attackers created "DDOS protection" companies to protect their victims against DDOS.

woodruffw 21 minutes ago

vlovich123 7 hours ago

> Update — August 4, 2026, 13:37 CEST: At least 868 packages (across 1381 versions) have been compromised by the worm, with a combined total of over 2 billion monthly installs at the time of writing.

This is such lazy or click baiting writing. Who cares how many installations there are per month normally? The high install numbers are almost certainly from running in CI where such secrets don’t exist. How many installs actually occur in a non CI environment and of those how many were the compromised version?

insanitybit 6 hours ago

> almost certainly from running in CI where such secrets don’t exist.

CI usually has the most privileged secrets anywhere in a company lol

TeMPOraL 6 hours ago

CIs having per-package installs : version ratio > 1 are fundamentally dumb idea in the first place.

In a way, this teaches us that there is something as too much reliability in an ecosystem. Specifically, Github is apparently reliable enough that its occasional outages are insufficient to kill all the companies running these dumb CI setups that redownload packages from the Internet every build - so there's no incentive to reduce waste and improve security globally.

rcxdude 5 hours ago