Self Propagating NPM Malware Compromises over 40 Packages
160 comments
·September 16, 2025jamesberthoty
redbell
Related (7 days ago):
NPM debug and chalk packages compromised (1366 points, 754 comments): https://news.ycombinator.com/item?id=45169657
codemonkey-zeta
I'm coming to the unfortunate realizattion that supply chain attacks like this are simply baked into the modern JavaScript ecosystem. Vendoring can mitigate your immediate exposure, but does not solve this problem.
These attacks may just be the final push I needed to take server rendering (without js) more seriously. The HTMX folks convinced me that I can get REALLY far without any JavaScript, and my apps will probably be faster and less janky anyway.
lucideer
> I'm coming to the unfortunate realizattion that supply chain attacks like this are simply baked into the modern JavaScript ecosystem.
I see this odd take a lot - the automatic narrowing of the scope of an attack to the single ecosystem it occurred in most recently, without any real technical argument for doing so.
What's especially concerning is I see this take in the security industry: mitigations put in place to target e.g. NPM, but are then completely absent for PyPi or Crates. It's bizarre not only because it leaves those ecosystems wide open, but also because the mitigation measures would be very similar (so it would be a minimal amount of additional effort for a large benefit).
woodruffw
Could you say more about what mitigations you’re thinking of?
I ask because think the directionality is backwards here: I’ve been involved in packaging ecosystem security for the last few years, and I’m generally of the opinion that PyPI has been ahead of the curve on implementing mitigations. Specifically, I think widespread trusted publishing adoption would have made this attack less effective since there would be fewer credentials to steal, but npm only implemented trusted publishing recently[1]. Crates also implemented exactly this kind of self-scoping, self-expiring credential exchange ahead of npm.
(This isn’t to malign any ecosystem; I think people are also overcorrect in treating this like a uniquely JavaScript-shaped problem.)
[1]: https://github.blog/changelog/2025-07-31-npm-trusted-publish...
kees99
I agree other repos deserve a good look for potential mitigations as well (PyPI too, has a history of publishing malicious packages).
But don't brush off "special status" of NPM here. It is unique in that JS being language of both front-end and back-end, it is much easier for the crooks to sneak in malware that will end up running in visitor's browser and affect them directly. And that makes it a uniquely more attractive target.
weinzierl
Which mitigations specifically are in npm but not in crates.io?
As far as I know crates.io has everything that npm has, plus
- strictly immutable versions[1]
- fully automated and no human in the loop perpetual yanking
- no deletions ever
- a public and append only index
Go modules go even further and add automatic checksum verification per default and a cryptographic transparency log.
Contrast this with docker hub for example, where not even npm's basic properties hold.
So, it is more like
docker hub ⊂ npm ⊂ crates.io ⊂ Go modules
[1] Nowadays npm has this arguably too
reactordev
Until you go get malware
Supply chain attacks happen at every layer where there is package management or a vector onto the machine or into the code.
What NPM should do if they really give a shit is start requiring 2FA to publish. Require a scan prior to publish. Sign the package with hard keys and signature. Verify all packages installed match signatures. Semver matching isn’t enough. CRC checks aren’t enough. This has to be baked into packages and package management.
lycopodiopsida
> Until you go get malware
While technically true, I have yet to see Go projects importing thousands of dependencies. They may certainly exist, but are absolutely not the rule. JS projects, however...
We have to realize, that while supply chain attacks can happen everywhere, the best mitigations are development culture and solid standard library - looking at you, cargo.
I am a JS developer by trade and I think that this ecosystem is doomed. I absolutely avoid even installing node on my private machine.
homebrewer
Here's an example off the top of my mind:
psychoslave
How will multi-factor-authentication prevent such a supply chain issue?
That is, if some attacker create some dummy trivial but convenient package and 2 years latter half the package hub depends on it somehow, the attacker will just use its legit credential to pown everyone and its dog. This is not even about stilling credentials. It’s a cultural issue with bare blind trust to use blank check without even any expiry date.
cxr
If NPM really cared, they'd stop recommending people use their poorly designed version control system that relies on late-fetching dependencies that are required by the build step, and they'd advise people to pick a reliable and robust VCS like Git for tracking/storing/retrieving source code objects and stick to that. This will never happen.
NPM has also been sending out nag emails for the last 2+ years about 2FA. If anything, that probably helped the attack on the Junon account that we saw a couple weeks ago.
ptx
NPM lock files seem to include hashes for integrity checking, so as long as you check the lock file into the VCS, what's the difference?
floydnoel
NPM does require 2FA to publish. I would love a workaround! Isn't it funny that even here on HN, misinformation is constantly being spread?
tarruda
AFAICT, the only thing this attack relies on, is the lack of scrutiny by developers when adding new dependencies.
Unless this lack of scrutiny is exclusive to JavaScript ecosystem, then this attack could just as well have happened in Rust or Golang.
coldpie
I don't know Go, but Rust absolutely has the same problem, yes. So does Python. NPM is being discussed here, because it is the topic of the article, but the issue is the ease with which you can pull in unvetted dependencies.
Languages without package managers have a lot more friction to pull in dependencies. You usually rely on the operating system and its package-manager-humans to provide your dependencies; or on primitive OSes like Windows or macOS, you package the dependencies with your application, which involves integrating them into your build and distribution systems. Both of those involve a lot of manual, human effort, which reduces the total number of dependencies (attack points), and makes supply-chain issues like this more likely to be noticed.
The language package managers make it trivial to pull in dozens or hundreds of dependencies, straight from some random source code repository. Your dependencies can add their own dependencies, without you ever knowing. When you have dozens or hundreds of unvetted dependencies, it becomes trivial for an attacker to inject code they control into just one of those dependencies, and then it's game over for every project that includes that one dependency anywhere in their chain.
It's not impossible to do that in the OS-provided or self-managed dependency scenario, but it's much more difficult and will have a much narrower impact.
null
hsbauauvhabzb
JavaScript does have some pretty insane dependency trees. Most other languages don’t have anywhere near that level of nestedness.
staminade
Don't they?
I just went to crates.io and picked a random newly updated crate, which happened to be pixelfix, which fixes transparent pixels in pngs.
It has six dependencies and hundreds of transient dependencies, may of which appear to be small and highly specific a la left-pad.
https://crates.io/crates/pixelfix/0.1.1/dependencies
Maybe this package isn't representative, but it feels pretty identical to the JS ecosystem.
rixed
This makes little sense. Any popular language with a lax package management culture will have the exact same issue, this has nothing to do with JS itself. I'm actually doing JS quasi exclusively these days, but with a completely different tool chain, and feel totally unconcerned by any of these bi-weekly NPM scandals.
cxr
It's not possible for a language have an insane dependency tree. That's an attribute of a codebase.
hoppp
They are. Any language that depends heavily on package managers and lacks a standard lib is vulnerable to this.
At some point people need to realize and go back to writing vanilla js, which will be very hard.
The rust ecosystem is also the same. Too much dependence on packages.
An example of doing it right is golang.
rs186
Python and Rust both have decent std lib, but it is just a matter of time before this happens in thoae ecosystems. There is nothing unique about this specific attack that could only happen in JavaScript.
jddj
Is the difference between the number of dev dependencies for eg. VueJs (a JavaScript library for marshalling Json Ajax responses into UI) and Htmx (a JavaScript library for marshalling html Ajax responses into UI) meaningful?
There is a difference, but it's not an order of magnitude and neither is a true island.
Granted, deciding not to use JS on the server is reasonable in the context of this article, but for the client htmx is as much a js lib with (dev) dependencies as any other.
https://github.com/bigskysoftware/htmx/blob/master/package.j...
everdrive
Javascript is badly over-used and over-depended on. So many websites just display text and images, but have extremely heavy javascript libraries because that's what people know and that is part of the default, and because it enables all the tracking that powers the modern web. There's no benefit to the user, and we'd be better off without these sites existing if there were really no other choice but to use javascript.
Aeolun
Why is this inevitable? If you use only easily verifyable packages you’ve lost nothing. The whole concept of npm automatically executing postinstall scripts was fixed when my pnpm started asking me every time a new package wanted to do that.
petcat
Rendering template partials server-side and fetching/loading content updates with HTMX in the browser seems like the best of all worlds at this point.
koakuma-chan
Until you need to write JavaScript?
bdcravens
Then write it. Javascript itself isn't the problem, naive third-party dependencies are.
baq
Which should be much less than what’s customary?
ehnto
But that's the neat part, you don't!
Meneth
This happens because there's no auditing of new packages or versions. The distro's maintainer and the developer is the same person.
The general solution is to do what Debian does.
Keep a stable distro where new packages aren't added and versions change rarely (security updates and bugfixes only, no new functionality). This is what most people use.
Keep a testing/unstable distro where new packages and new versions can be added, but even then added only by the distro maintainer, NOT by the package developers. This is where the audits happen.
NPM, Python, Rust, Go, Ruby all suffer from this problem, because they have centralized and open package repositories.
weinzierl
In Rust we have cargo vet, where we share these audits and use them in an automated fashion. Companies like Google and Mozilla contribute their audits.
rixed
Exactly, in a way Debian (or any other distro) is an extended standard library.
Aeolun
> suffer from this problem
Benefit from this feature.
homebrewer
When the left-pad debacle happened, one commenter here said of a well known npm maintainer something to the effect of that he's an "author of 600 npm packages, and 1200 lines of JavaScript".
Not much has changed since then. The best counter-example I know is esbuild, which is a fully featured bundler/minifier/etc that has zero external dependencies except for the Go stdlib + one package maintained by the Go project itself:
https://www.npmjs.com/package/esbuild?activeTab=dependencies
https://github.com/evanw/esbuild/blob/755da31752d759f1ea70b8...
Other "next generation" projects are trading one problematic ecosystem for another. When you study dependency chains of e.g. biomejs and swc, it looks pretty good:
https://www.npmjs.com/package/@biomejs/biome/v/latest?active...
https://www.npmjs.com/package/@swc/types?activeTab=dependenc...
Replacing the tire fire of eslint (and its hundreds to low thousands of dependencies) with zero of them! Very encouraging, until you find the Rust source:
https://github.com/biomejs/biome/blob/a0039fd5457d0df18242fe...
https://github.com/swc-project/swc/blob/6c54969d69551f516032...
I think as these projects gain more momentum, we will see similar things cropping up in the cargo ecosystem.
Does anyone know of other major projects written in as strict a style as esbuild?
cookiengineer
Part of the reason of my switch to using Go as my primary language is that there's this trend of purego implementations which usually aim towards zero dependencies besides the stdlib and golang.org/x.
These kind of projects usually are pretty great because they aim to work with CGO_ENABLED=0 so the libs are very portable and work with different syscall backends.
Additionally I really like to go mod vendor my snapshot of dependencies which is great for short term fixes, but it won't fix the cause in the long run.
However, the go ecosystem is just as vulnerable here because of lack of signing off package updates. As long as there's no verification possible end-to-end when it comes to "who signed this package" then there's no way this will get better.
Additionally most supply chaib attacks focussed on the CI/CD infrastructure in the past, because they are just as broken with just as many problems. There needs to be a better CI/CD workflow where signing keys don't have to be available on the runners themselves, otherwise this will just shift the attack surface to a different location.
In my opinion the package managers are somewhat to blame here, too. They should encourage and mandate gpg signatures, and especially in git commits when they rely on git tags for distribution.
benmccann
Yes, eslint is particularly frustrating: https://npmgraph.js.org/?q=eslint
There are plenty of people in the community who would help reduce the number of dependencies, but it really requires the maintainers to make it a priority. Otherwise the only way to address it is to switch to another solution like oxlint.
zelphirkalt
The answer is to not draw in dependencies for things you are easily able to write yourself. That would probably reduce dependencies by 2/3 or so in many projects. Especially, left-pad things. If you write properly self contained small parts and a few tests, you probably don't have to touch them much, and the maintenance burden is not that high. Compare that with having to check every little dependency like left pad and all its code and its dependencies. If a dependency is not strictly necessary, then don't do it.
GuB-42
> Shai Hulud
Clever name... but I would have expected malware authors to be a bit less obvious. They literally named their giant worm after a giant worm.
> At the core of this attack is a ~3.6MB minified bundle.js file
Yep, even malware can be bloated. That's in the spirit of NPM I guess...
jsheard
I suppose it's only a matter of time before one of these supply chain attacks gets supply chain attacked.
chillax
According to Aikido Security the attack has now targeted 180+ packages: https://www.aikido.dev/blog/s1ngularity-nx-attackers-strike-...
jbd0
I knew npm was a train wreck when I first used it years ago and it pulled in literally hundreds of dependencies for a simple app. I avoid anything that uses it like the plague.
oVerde
So basically you live JavaScript free?
null
Xelbair
as much as i can yes.
I try to avoid JS, as it is a horrible language, by design. That does include TS, but it at least is useable, but barely - because it still tied to JS itself.
diggan
Off-topic, but I love how different programmers think about things, and how nothing really is "correct" or "incorrect". Started thinking about it because for me it's the opposite, JS is an OK and at least usable language, as long as you avoid TS and all that comes with it.
Still, even I who'd call myself a JavaScript developer also try to avoid desktop applications made with just JS :)
hoppp
Lucky you. I keep coming back to it because jobs and even for desktop apps a native webview beats everything else.
We fcked up with js, big time and its with us forever now
kaiomagalhaes
out of sincere curiosity, which one is a great programming language to you?
shkkmo
You can write javascript without using npm...
Arch-TK
I mean, it's hard to avoid indirectly using things that use npm, e.g. websites or whatever. But it's pretty easy to never have to run npm on your local machine, yes.
epolanski
"I knew you weren't a great engineer the moment you started pulling dependencies for a simple app"
You realize my point right? People are taught to not reinvent the wheel at work (mostly for good reasons) so that's what they do, me and you included.
You ain't gonna be bothered to write html and manual manipulation, the people that will give you libraries to do so won't be bothered reimplementing parsers and file watchers, file watcher writers won't be bothered reimplementing file system utils, file system utils developers won't be bothered reimplementing structured cloning or event loops, etc, etc.
I myself just the other day had the task of converting HTML to markdown, because I don't remember whether it was Jira or Github APIs that returns comments as HTML and despite it being mostly few hours of work that would get us 90% there everybody was in favor of pulling a dependency to do so (with its own dependencies) and thus further exposing our application to those risks.
zachrip
I can tell a lot about a dev by the fact that they single out npm/js for this supply chain issue.
brobdingnagians
Lots of languages ecosystems have this problem, but it is especially prominent in JS and lies on a spectrum. For comparison, in the C/C++ ecosystem it is prominent to have libraries advertising that they have zero dependencies and header only or one common major library like Boost.
RUnconcerned
What other language ecosystems have had this happen systematically? This isn't even the first time this month!
Aeolun
I think it’s just that a lot of old men don’t like how popular it has become with script kiddies.
lithos
Just more engineering leaning than you. Actual engineers have to analyze their supply chains, and so makes sense they would be baffled by NPM dependency trees that utterly normal projects grow into in the JavaScript ecosystem.
zachrip
Do you think companies using node don't analyze supply chains? That's nonsense. Have you cargo installed a rust app recently? This isn't just a js issue. This needs to be solved across the industry and npm frankly has done a horrible job at it. We let people with billions of downloads a month with recently changed password/2fa publish packages? Why don't we pool assets as a collective to scan newly published packages before they're allowed to be installed? These types of things really should exist across all package registries (and my really hot take is that we probably don't need a registry for every language, either!).
hsbauauvhabzb
That they’ve coded in more than one language?
null
thegeomaster
Warning: LLM-generated article, terribly difficult to follow and full of irrelevant details.
madeofpalk
My main takeaway from all of these is to stop using tokens, and rely on mechanisms like OIDC to reduce the blast radius of a compromise.
How many tokens do you have lying around in your home directory in plain text, able to be read by anything on your computer running as your user?
diggan
> How many tokens do you have lying around in your home directory in plain text, able to be read by anything on your computer running as your user?
Zero? How many developers have plain-text tokens lying around on disk? Avoiding that been hammered into me from every developer more senior than me since I got involved with professional software development.
madeofpalk
You're sure you don't have something lying around in ~/.config ? Until recently the github cli would just save its refresh token as a plain text file. AWS CLI loves to have secrets sitting around in a file https://docs.aws.amazon.com/cli/latest/userguide/cli-configu...
diggan
I don't use AWS and looking in ~/.config/gh I see two config files, no plain-text secrets.
With that said, it's not impossible some tool leaks their secrets into ~/.local, ~/.cache or ~/.config I suppose.
I thought they were referencing the common approach of adding environment variables with plaintext secrets to your shell config or as an individual file in $HOME, which been a big no-no for as long as I can remember.
I guess I'd reword it to "I'm not manually putting any cleartext secrets on disk" or something instead, if we wanted it to be 100% accurate.
viraptor
> How many developers have plain-text tokens lying around on disk?
Most of them. Mainly on purpose, (.env files) but many also accidentally. (shell history with tokens in the commands)
pjc50
Isn't this quite hard to achieve on local systems, where you don't have a CI vault automation to help?
madeofpalk
It's not that hard if it's something you decide you care about and want to solve. Like diggan mentions, there's many tools, some you already might use, that can be used to inject secrets into applications that's not too onerous to use in your development workflow.
diggan
I don't think so? I don't even know what a "CI vault automation" is, I store my credentials and secrets in 1Password, and use the CLI to get the secrets for the moments they're needed, I do all my development locally and things seem fine.
mewpmewp2
How do you manage secrets for your projects?
mr_toad
One option is pass, which is a shell script that uses GPG to manage passwords for command line tools. You can put the password store into a git repository if you need to sync it across machines.
diggan
Using a password manager for fetching them when needed. 1Password in my case, but I'm sure any password manager can be used for storing secrets for most programming projects.
tormeh
A good habit, but encryption won't save you in all cases because anything you run has write access to .bashrc.
Frankly, our desktop OSes are not fit for purpose anymore. It's nuts that everything I run can instantly own my entire user account.
It's the old https://xkcd.com/1200/ . That's from 2013 and what little (Flatpak, etc.) has changed has only changed for end users - not developers.
pingou
As a developer, is there a way on mac to limit npm file access to the specific project? So that if you install a compromised package it cannot access any data outside of your project directory?
mfro
Frankly, I am refusing to use npm outside of docker anymore.
danieldspx
Just notice guys it did not started with tinycolor. I had first reported it here, I am just not as popular haha
My posts way before the issue was created: https://news.ycombinator.com/item?id=45252940 https://www.linkedin.com/posts/daniel-pereira-b17a27160_i-ne...
nahuel0x
Languages/VMs should support capability-based permissions for libraries, no library should be able to open a file or do network requests without explicit granular permissions.
A lot of blogs on this are AI generated and such as this is developing, so just linking to a bunch of resources out there:
Aikido - https://www.aikido.dev/blog/s1ngularity-nx-attackers-strike-...
Socket - https://socket.dev/blog/ongoing-supply-chain-attack-targets-...
Ox - https://www.ox.security/blog/npm-2-0-hack-40-npm-packages-hi...
Safety - https://www.getsafety.com/blog-posts/shai-hulud-npm-attack
Phoenix - https://phoenix.security/npm-tinycolor-compromise/
Semgrep - https://semgrep.dev/blog/2025/security-advisory-npm-packages...