It's time pre-install / post-install hooks were killed off. Start with a moratorium on any new ones.
The idea is to have a local proxy that injects real secrets into requests in-flight, so a compromised library has nothing to steal because it never had access to any of your secrets: https://github.com/inflightsec/agent-vault-proxy
In other words: people should be auditing their software, but we should also design systems and schemes that provide varying degrees of defense and protection when people invariably fail to review the code they run.
Sure, solve the problem of "auditing and trusting codebases". Go for it. Shouldn't take you until, oh, let me be generous and give you until next Monday. No sweat.
(if you want a better mitigation: don't automatically update dependencies in CI. At a minimum have a cooling-off period that you only bypass on manual review. This is not hard to implement and at least gives some time for alarm bells to be sounded before you're pwned. Probably while doing the updates you can also just do a quick sanity check on the changes, that'll also catch the ones using a blatant install hook)
One of the problems a lot of companies are having right now is trying to determine a proper window size between CVE release and patching.
On one hand, you have LLM created 0days and attacks are happening almost as fast as CVEs can be posted, and CVEs are being posted at lightning speed. You maybe had a month or a few weeks to patch before, now the window may be down to days or even hours.
On the other hand, you want to prevent these repo bombing attacks.
There's a tension there but I think we're going to end up measuring that window in hours within the year, if we're not already there.
I mean that's a big window of time now.
(If you find you do need updates when you're sleeping, you probably need to delegate that process or at least the process of checking the update to someone you trust, and you're probably paying for the service. This blog post is in large part an ad for just such a service).
Man, I wish.
Anti-tank measures are unnecessary because they can just use missiles
(if you want to disable such hooks yourself, then you may get some security by diversity because you're not using the common configuration. But if it becomes the default then these worms will switch to a different vector)
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.
To be clear, just solving the CI/CD portion is insufficient, but it is a massive win.
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).
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.
This should be your default minimum if you work with node.
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/
For example:
find . -type f | grep -P "/Math_Symbol\.js$" fd -HI "^(setup\.mjs|Math_Symbol\.js|math_init\.js)$"
1. https://github.com/sharkdp/fd2. `brew install fd`
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.
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.
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.
https://github.blog/changelog/2026-07-28-npm-publish-time-ma...
> This requirement will be progressively enforced over time.
I am curious if they are still implementing the process or if this particular attacker already figured out a way around it.
Maybe that's the idea. Regards, the <insert your favourite 3 letter agency here>
Unless...
Maybe in 6 months.
LLMs aren't terrible at securing systems, humans are bad at using secure systems and typically disable measures with insecure workarounds.
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.
NPM gets targeted a lot because it's popular. That's it.
You’re exposed as a function of the number of dependencies so the communities which most normalize many rapidly updating packages are going to be at greater risk. That’s not a simple trade off — that enterprise Java app which updates on a decadal cadence is still worse — but it means you need to accept the risk and use other mitigations.
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.
(I have no opinion about the overall security posture of these indices.)
I just think the framing that npm is so bad is really flatly invalid.
Is it really though if we're getting thousands of compromised packages regularly?
You can do all the right things and still be legit problematic.
(Rust has a similar culture, to be clear. I don’t think it’s a death knell.)
sudo apt-cache stats
Total package names: 162150 (5189 k) Total package structures: 145852 (6417 k) Normal packages: 69505 Pure virtual packages: 1169 Single virtual packages: 64848 Mixed virtual packages: 355 Missing: 9975 Total distinct source versions: 71172 (1708 k) Total distinct versions: 71172 (6334 k) Total distinct descriptions: 139719 (3353 k) Total dependencies: 435057/121405 (10.6 M) Total ver/file relations: 73378 (1174 k) Total Desc/File relations: 142692 (2283 k) Total Provides mappings: 70533 (1693 k) Total globbed strings: 268430 (6560 k) Total slack space: 77.3 k Total space accounted for: 47.0 M Total buckets in PkgHashTable: 196613 Unused: 93545 Used: 103068 Utilization: 52.4218% Average entries: 1.4151 Longest: 19 Shortest: 1 Total buckets in GrpHashTable: 196613 Unused: 86025 Used: 110588 Utilization: 56.2465% Average entries: 1.46625 Longest: 7 Shortest: 1
- 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.
What do you mean? The lockfile of all package managers is there for pinning the exact versions. For yarn and pnpm, installs on CI run automatically from lockfile only, for npm I think you still need to run `npm ci` instead of `npm install`. But this guarantees that no new versions get pulled automatically in by CI.
> Typescript / Javascript's lack of a standard library
That's true, but it's not an issue of the package manager / registry
> Post install scripts execute external code by default upon downloading dependencies.
They are disabled by default in all package managers now, the user needs to manually allow them
How many instances of this are required before npm package maintainers learn?
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.
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.
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.
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.
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.
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
(I also dare say: many of these attackers demonstrate a better in-depth understanding of packaging ecosystems than supply chain security vendors do.)
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.
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?
CI usually has the most privileged secrets anywhere in a company lol
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.