Rendered at 06:56:46 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
xnorswap 17 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 16 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 16 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 15 hours ago [-]
These half-measures is why everything has gone to shits. Instead of properly auditing software, reducing the quantity and increasing the quality, we keep pushing more and more garbage where all you find is 2FA that, captcha this, not supported this, app not signed, etc..
radku 10 hours ago [-]
Can't agree more. I'm working on OSS security tool that can protect you from Shai-hulud, no half measures.
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
woodruffw 13 hours ago [-]
Defense in depth is the “meat and potatoes” of security.
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.
rcxdude 13 hours ago [-]
Defence in depth is not just 'throw anything in that might improve security' though. The idea is to have multiple strong layers, not a hundred half-measures that are all easily bypassed. A stronger layer might be sandboxing, or separating your build and publishing steps as others have suggested (and also probably worth restricting the credentials the publishing step to just the relevant packages as well). These will at least robustly prevent a malicious dependency from spreading horizontally, but you'll still potentially ship malware to your customers.
msm_ 8 hours ago [-]
>The idea is to have multiple strong layers, not a hundred half-measures that are all easily bypassed
Definitely. On the other hand, in my opinion, "not running arbitrary code during package install" is not a "half-measure", it's a basic sanity. This whole arbitrary code execution at install time is a convenience feature that was adapted by some package managers, but it was never a good idea.
Fortunately, nixos solves that for me in most cases.
woodruffw 13 hours ago [-]
I would consider 2FA and signing to be strong layers, when applied well. I think everybody agrees we shouldn’t add layers just for the sake of it.
rcxdude 16 hours ago [-]
The fact that it's not sufficient also largely means it's not necessary either, because the real solution is auditing and trusting the codebase as a whole. All you do when disabling install hooks is make a lot of situations much more difficult to handle.
jerf 16 hours ago [-]
You will also not get very far in engineering if you have a production incident, someone proposes a thing that will mitigate it partially but significantly, and you insist that we can't deploy that mitigation because we need to do the multi-year project that will actually fix it instead. Even if we still need that project, we also need that mitigation.
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.
rcxdude 15 hours ago [-]
Feel free to turn it off yourself, just don't be surprised when the attackers switch tactics. And don't make life harder for everyone else by pushing it as mandatory.
(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)
jerf 7 hours ago [-]
I mostly use languages where the feature doesn't even exist. We don't generally miss it.
I also blocked a proposed mandatory dependabot at work a few months back because I didn't like the way it created a pipeline for any hacker to push a hacked dependency straight into someone's codebase. I'm lifting my objection now that dependabot is defaulting to a 3-day cooldown, though the code bases I'm managing I'm setting to 7 days. (Not to be behind everyone else; I'd be fine if everyone joined me at 7 days. I don't really accept the freeloading objection, there's plenty of entities scanning things now no matter what cooldown you set. I just think 3 is a little tight to expect the full discovery and remediation to take place.)
So, whatever sort of "but it's really hard! I bet you don't do it yourself" implications you may be trying to draw fall very flat. Or whatever you may have been trying to imply about trusting this step but not trusting others... no, I do defense in depth. Giving up on defense in depth because one step isn't enough... well, I think I've probably played the "you won't get very far in engineering" card enough, but hey, here's one last time.
esseph 14 hours ago [-]
> 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.
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.
rcxdude 14 hours ago [-]
If you need quick responses to such things, you're gonna need some intelligence in the loop regardless, even if it's just deciding when it's worth pushing a new release to prod.
esseph 13 hours ago [-]
What if it needs to get patched and pushed while you and everyone else in the org are asleep?
I mean that's a big window of time now.
rcxdude 13 hours ago [-]
Always has been, arguably. Either way I don't think most people have their CI configured to automatically re-run when a dependency updates (imagine the thundering herd!), the compromises are more incidental to runs happening for other reasons, and that configuration neither guarantees a quick response nor protects against supply chain attacks.
(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).
BigTTYGothGF 14 hours ago [-]
> You will also not get very far in engineering if
Man, I wish.
lkjdsklf 15 hours ago [-]
Missile defense systems are unnecessary because they could just use tanks
Anti-tank measures are unnecessary because they can just use missiles
rcxdude 14 hours ago [-]
What's your solution to using a library without executing the code in it?
msm_ 8 hours ago [-]
This is moving the goalposts. The original problem was that installing a library should not execute code from that library.
In most sane environments, like for example native languages, this is already the case. Downloading a .dll file and putting it in an appropriate directory won't, by itself, execute code in that library.
You may argue that the code will get executed at some point anyway, but that's besides the point. Sandboxing the build environment is a different problem than sandboxing the test/staging/production environment.
I think we both agree that "adding random obstacles that don't actually protect anything" is not a valid approach to security, but my mental model of the build step is "transformation of input data into output data", and while this step may produce a malicious output from malicious inputs, it should not do anything malicious itself. For example, "gcc source.c" should not execute arbitrary code by itself.
rcxdude 29 minutes ago [-]
>In most sane environments, like for example native languages, this is already the case.
Installing a node package is much more like compiling a dll, not downloading it. The same is true for most package managers that exist for C and C++ as languages as opposed to for an OS. These are two different tools for different use-cases. (though still pretty much all installation processes for all OSs involve an opportunity for arbitrary code execution, as well, apart from just downloading a zip file and extracting it, which is not the norm)
fhdkweig 15 hours ago [-]
The term you are looking for is Defense in Depth/Layers
rcxdude 15 hours ago [-]
That makes sense if it's actually a layer. You can create a thousand minor obstacles and it still won't be a very secure system.
jitl 16 hours ago [-]
"make no mistakes" is not the "real solution"
rcxdude 16 hours ago [-]
Neither is 'close the gate with no fence on either side of it'. If you want to run code, you either need to run it in a sandbox or trust it. Choosing to run only part of the code is not really a solution.
(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)
insanitybit 16 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 16 hours ago [-]
Wouldn't the dropper get executed once tests are run within CI though?
insanitybit 15 hours ago [-]
Yes, you should separate "tests execute" into their own unprivileged workflows that don't have "deploy" secrets.
rcxdude 15 hours ago [-]
You can also do the same for the build workflow, no?
insanitybit 14 hours ago [-]
Yep, I'd recommend it.
jitl 16 hours ago [-]
yeah, or when a dev starts the local development server (unless that server is containerized).
insanitybit 15 hours ago [-]
Dev laptops tend to have better monitoring than CI/CD so I still think this is a better option. You can also have devs use VMs or separate dev environments like an ec2 instance.
To be clear, just solving the CI/CD portion is insufficient, but it is a massive win.
sysguest 16 hours ago [-]
well Deno has the necessary ingredients for defense: file-system permission by path
mechazawa 17 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 14 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_ 14 hours ago [-]
npm v12 released last month also defaults into blocking them by default
insanitybit 16 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).
ChrisMarshallNY 14 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.
mittermayr 17 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)?
rhdunn 16 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 15 hours ago [-]
Crucially, Math_Symbol.js that is almost 800KB, not the innocent 1KB one from regenerate-unicode-properties
somebudyelse 14 hours ago [-]
when i was searching i got a heartattack when i saw Math_Symbol.js. Thankfully my agent was able to figure it out.
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.
mplewis 8 hours ago [-]
No one wants to see slop that you pooped out of an LLM butt
Packj uses static+dynamic code/behavioral analysis to scan for indicators of compromise (e.g., spawning of shell, use of SSH keys, network communication, use of decode+eval, etc). It also checks for several metadata attributes to detect impersonating packages (typo squatting).
koalak 7 hours ago [-]
[dead]
TimJRobinson 7 hours ago [-]
It's time for all developers to learn about devcontainers and use them consistently. They're super easy to setup and run and would protect from most of what this worm does. https://code.visualstudio.com/docs/devcontainers/containers is the best guide to get started if you use vscode.
acdha 7 hours ago [-]
They help only to the extent that you have completely isolated credentials: the hard part isn’t the container, it’s things like fastidiously using separate least-privilege credentials everywhere and not using tools or editor integrations which don’t support that style of work. Once you map your GitHub or AWS token into a container, it’s no longer useful as a security boundary.
jesse_dot_id 13 hours ago [-]
echo "min-release-age=5" >> ~/.npmrc
This should be your default minimum if you work with node.
freakynit 12 hours ago [-]
Updated my docs covering these attacks since 2025:
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?
Yes, it gets boring that each time one of these supply chain attack article appears everybody starts talking about cooldowns, 2FA, MFA, etc. Just don't give Node the permissions to your complete filesystem / network.
acdha 7 hours ago [-]
I think it’s premature before a lot of tools improve to make that more workable: for example, if you use AWS how realistic is maintain separate IAM for each tool you run and map the right one into a sandbox for each tool? To use your editor’s GitHub integration with a token which can do basic operations and only retrieves a high-privilege token with a hardware presence check when you cut a release?
Theoretically you can do it but the friction is enough to make it non-viable.
evertheylen 16 minutes ago [-]
Could you not use something like https://github.com/superfly/tokenizer for AWS? They list it as an explicit example, but I have little experience with AWS.
phyzome 8 hours ago [-]
Couldn't GitHub detect a Shai-Hulud exfil repo being created and just... block it? Given that it's a worm, the attacker wouldn't be able to adapt all that quickly.
cookiengineer 6 hours ago [-]
We're talking about Microsoft, who created a notepad.exe that can run an RCE with an LLM bypass prompt.
In the previous Miasma waves, Microsoft was so overwhelmed that they delayed the VSCode extension installs for a couple days with a timeout; literally not understanding what was going on and neither how the malware was spreading.
richbell 6 hours ago [-]
It's baffling that this hasn't been done after the 6th(?) time in the past year.
avaer 18 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.
I am curious if they are still implementing the process or if this particular attacker already figured out a way around it.
twistedpair 11 hours ago [-]
Ah, I see your problem there
> This requirement will be progressively enforced over time.
hulitu 17 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>
codeduck 16 hours ago [-]
Oh boy, it's a big one.
TacticalCoder 16 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.
somebudyelse 15 hours ago [-]
the irony of the update being at 1337
twistedpair 11 hours ago [-]
2,523 and counting
ethanwillis 16 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 14 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.
cookiengineer 5 hours ago [-]
yawns Good morning world,
Here's the updated Antimiasma tool for mitigation [1] [2]. More details on how this tool was built and how the Miasma worm works on my website [3].
This is the first false flag in the campaign series, where setting the "LANG" environment variable to "ru_RU.UTF-8" or "ru_RU.KOI8-R" won't stop the spreading mechanism.
So it's likely this could've been any script kiddie that modified the TeamPCP source code dump. It could now also be still APT28/29, that was kind of the purpose of the code dump... to gain plausible deniability :)
what commercial tools are enterprises using today to defend against such attacks? Do they really work? I mean, do they report/block malware after the fact or detect proactively. Because if the latter then, package registries should really be removing reported packages, right?
15 hours ago [-]
tomjen3 12 hours ago [-]
Where is the fbi in this? Why has no one been arrested? This is a massive crime.
egonschiele 10 hours ago [-]
I'm guessing (though not sure) the actors are outside the US.
cookiengineer 4 hours ago [-]
FBI has no jurisdiction over Russia.
And Russia doesn't give a damn, as they literally gave APT28/29 the mandate to do this, the only exception being that no former Sovjet territories can be attacked.
(With the current exception of Ukraine ofc).
Catloafdev 15 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?
thinkingemoji 16 hours ago [-]
No way to prevent this says only package manager where this regularly happens
insanitybit 15 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 14 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 14 hours ago [-]
I think that's barely meaningful. Which of the compromised packages would have been part of any reasonable stdlib?
acdha 12 hours ago [-]
It’s not that there’s a single stdlib feature which would’ve stopped this but more that JavaScript developers have been conditioned that it’s normal to install tons of packages and update them quite frequently so there are a lot of individual maintainers who if compromised have a surprising impact.
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.
insanitybit 11 hours ago [-]
I just don't think that this is that unique to javascript, it's absolutely not about npm, and I don't think that this is well supported as a relevant feature that leads to these attacks.
altcognito 15 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 14 hours ago [-]
Arguably crates.io is worse. NPM has cooldowns and has for a while, it has had Trusted Publishing for longer, it has human-approved releases that separate CI/CD from actual publishing. Ruby is probably worse in every way.
woodruffw 13 hours ago [-]
RubyGems actually adopted Trusted Publishing before both npm and crates.io. To my recollection, they were second after PyPI.
(I have no opinion about the overall security posture of these indices.)
insanitybit 13 hours ago [-]
No build script control though.
woodruffw 13 hours ago [-]
Yep. That remains the norm with Python source distributions as well. It’s a hard thing to overcome when it’s baked deeply into packaging assumptions.
insanitybit 13 hours ago [-]
Yeah, my point is just that other package managers aren't in a great spot. NPM even lets you separate out "publish" and "release" now where you can publish to the registry but you have to separately "ack" that to release. That's kinda a huge win if people use it.
I just think the framing that npm is so bad is really flatly invalid.
altcognito 12 hours ago [-]
> 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.
insanitybit 11 hours ago [-]
Yes, it has nothing to do with the design of npm (relative to similar languages/ repositories) and everything to do with the popularity.
woodruffw 12 hours ago [-]
I don’t think npm itself is measurably bad in ways that are useful to index on. Like others, I think that JS as an ecosystem has a culture that encourages both dependency sprawl and living at the edge, both of which contribute to the end problem of OSS malware.
(Rust has a similar culture, to be clear. I don’t think it’s a death knell.)
walrus01 15 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 14 hours ago [-]
I don't consider these comparable in any way that's worthwhile. The scale and goals are completely different.
walrus01 14 hours ago [-]
How are they not comparable?
insanitybit 14 hours ago [-]
Debian's scale for package distribution is tiny and explicitly curated by maintainers. Everything funnels through Debian. The goals are completely different. Debian packages what's necessary for an OS, npm packages everything needed for any projects arbitrarily. Auditing scales to one of those, not the other.
walrus01 14 hours ago [-]
The quantity of packages that exist at 162150 seems to say to me that there's a great many more than what are "necessary" for the operating system to exist.
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
insanitybit 13 hours ago [-]
162k vs millions. That's not even getting into package update velocity, authorship, the totally divergent goals, etc. I just think it's utterly pointless to compare.
lurkerforawhile 12 hours ago [-]
left-pad was over a decade ago. it's a problem with the registry itself, more than just the package manager.
insanitybit 11 hours ago [-]
left-pad is totally irrelevant to this conversation.
rvz 15 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 14 hours ago [-]
> Imported packages are not pinned by default
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
madeofpalk 14 hours ago [-]
Two out of three of your points - the first and last - are just incorrect.
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 13 hours ago [-]
ai slop comment. post the original prompt
daniel_mcsoft 15 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 13 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 12 hours 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.
vhcr 10 hours ago [-]
A compromised device still can't access the secure enclave.
pixl97 9 hours ago [-]
It doesn't need to, it just updates the code, you have the human publish.
freakynit 2 hours ago [-]
Updates should be changed to delete + publish, and either should require OTP/MFA. You don't need artificial cooldown if you add a manual, informed action in-between. All these publishes went uninformed to their maintainers.. that's the issue.
reindeer2 7 hours ago [-]
[flagged]
khanhnguyen8386 12 hours ago [-]
[flagged]
nubg 13 hours ago [-]
ad slop post
cute_boi 16 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 16 hours ago [-]
the remedy is worse than the disease
ftlps 17 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.
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 13 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 12 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 11 hours ago [-]
I don’t think the impact of these recent malware campaigns is below the threshold for federal interest.
Fnoord 7 hours ago [-]
That is not a suspicion, but a (baseless) theory. I mean, your proof is from Windows 98. Might as well been an outlier or urban legend, who knows.
My theory is that people in countries without extradition laws to USA are going to spray the shit out of Americans. With spam, and crap like this worm, troll farms, and the like.
Also baseless, but people who work at SOC can notice an increase since some changes happened in geopolitics this decade.
cadamsdotcom 5 hours ago [-]
That's an explosive accusation and frankly quite irresponsible.
Is this based on evidence, or are you just stoking anger at no one in particular?
vlovich123 18 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 17 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 17 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 16 hours ago [-]
Redownloading and automatically updating to the new version are pretty orthogonal. You can have a solution which caches the packages but would still update to a new version or a solution which redownloads the (verifiably) same package each time. Vendoring libraries stops both but it's the automatic updates which are the biggest risk factor.
cookiengineer 6 hours ago [-]
> CI usually has the most privileged secrets anywhere in a company lol
Literally the reason the stealer of Miasma was focusing on extracting tokens from the CI/CD runner from the start.
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.
Definitely. On the other hand, in my opinion, "not running arbitrary code during package install" is not a "half-measure", it's a basic sanity. This whole arbitrary code execution at install time is a convenience feature that was adapted by some package managers, but it was never a good idea.
Fortunately, nixos solves that for me in most cases.
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)
I also blocked a proposed mandatory dependabot at work a few months back because I didn't like the way it created a pipeline for any hacker to push a hacked dependency straight into someone's codebase. I'm lifting my objection now that dependabot is defaulting to a 3-day cooldown, though the code bases I'm managing I'm setting to 7 days. (Not to be behind everyone else; I'd be fine if everyone joined me at 7 days. I don't really accept the freeloading objection, there's plenty of entities scanning things now no matter what cooldown you set. I just think 3 is a little tight to expect the full discovery and remediation to take place.)
So, whatever sort of "but it's really hard! I bet you don't do it yourself" implications you may be trying to draw fall very flat. Or whatever you may have been trying to imply about trusting this step but not trusting others... no, I do defense in depth. Giving up on defense in depth because one step isn't enough... well, I think I've probably played the "you won't get very far in engineering" card enough, but hey, here's one last time.
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
In most sane environments, like for example native languages, this is already the case. Downloading a .dll file and putting it in an appropriate directory won't, by itself, execute code in that library.
You may argue that the code will get executed at some point anyway, but that's besides the point. Sandboxing the build environment is a different problem than sandboxing the test/staging/production environment.
I think we both agree that "adding random obstacles that don't actually protect anything" is not a valid approach to security, but my mental model of the build step is "transformation of input data into output data", and while this step may produce a malicious output from malicious inputs, it should not do anything malicious itself. For example, "gcc source.c" should not execute arbitrary code by itself.
Installing a node package is much more like compiling a dll, not downloading it. The same is true for most package managers that exist for C and C++ as languages as opposed to for an OS. These are two different tools for different use-cases. (though still pretty much all installation processes for all OSs involve an opportunity for arbitrary code execution, as well, apart from just downloading a zip file and extracting it, which is not the norm)
(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.
For example:
2. `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.
Packj uses static+dynamic code/behavioral analysis to scan for indicators of compromise (e.g., spawning of shell, use of SSH keys, network communication, use of decode+eval, etc). It also checks for several metadata attributes to detect impersonating packages (typo squatting).
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/
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.
Theoretically you can do it but the friction is enough to make it non-viable.
In the previous Miasma waves, Microsoft was so overwhelmed that they delayed the VSCode extension installs for a couple days with a timeout; literally not understanding what was going on and neither how the malware was spreading.
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...
I am curious if they are still implementing the process or if this particular attacker already figured out a way around it.
> This requirement will be progressively enforced over time.
Maybe that's the idea. Regards, the <insert your favourite 3 letter agency here>
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.
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.
Here's the updated Antimiasma tool for mitigation [1] [2]. More details on how this tool was built and how the Miasma worm works on my website [3].
This is the first false flag in the campaign series, where setting the "LANG" environment variable to "ru_RU.UTF-8" or "ru_RU.KOI8-R" won't stop the spreading mechanism.
So it's likely this could've been any script kiddie that modified the TeamPCP source code dump. It could now also be still APT28/29, that was kind of the purpose of the code dump... to gain plausible deniability :)
Anyways, stay safe folks.
[1] https://github.com/cookiengineer/antimiasma
[2] https://github.com/cookiengineer/antimiasma/releases/tag/mia...
[3] https://cookie.engineer/projects/cyber-defense/antimiasma.ht...
And Russia doesn't give a damn, as they literally gave APT28/29 the mandate to do this, the only exception being that no former Sovjet territories can be attacked.
(With the current exception of Ukraine ofc).
How many instances of this are required before npm package maintainers learn?
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
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.
My theory is that people in countries without extradition laws to USA are going to spray the shit out of Americans. With spam, and crap like this worm, troll farms, and the like.
Also baseless, but people who work at SOC can notice an increase since some changes happened in geopolitics this decade.
Is this based on evidence, or are you just stoking anger at no one in particular?
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.
Literally the reason the stealer of Miasma was focusing on extracting tokens from the CI/CD runner from the start.