Why cooldowns? Most npm (or pypi) compromises were taken down within hours, cooldowns simply mean - ignore any package with release date younger than N days (1 day can work, 3 days is ok, 7 days is a bit of an overkill but works too)
How to set them up?
- use latest pnpm, they added 1 day cooldown by default https://pnpm.io/supply-chain-security
- or if you want a one click fix, use https://depsguard.com (cli that adds cooldowns + other recommended settings to npm, pnpm, yarn, bun, uv, dependabot and, Iβm the maintainer)
- or use https://cooldowns.dev which is more focused on, well, cooldowns, with also a script to help set it up locally
All are open source / free.
If you know how to edit your ~/.npmrc etc, you don't really need any of them, but if you have a loved one who just needs a one click fix, these can likely save them from the next attack.
Caveat - if you need to patch a new critical CVE, you need to bypass the cooldown, but each of them have a way to do so. In the past few weeks, while I don't have hard numbers, it seems more risk has come from Software Supply Chain attacks (malicious versions pushed) than from new zero day CVEs (even in the age of Mythos driven vulnerability discovery)
But young blood mocked the fact to have to wait for Manual human review, safe gpg signatures, cool down periods and weeks of "testing" stage before being considered "stable".
And now most companies data are leaked and on the wide, hackers and ransomware are thriving...
This is crazy when you think about it because after so many years of software dev crafting experience, "modern safe" languages like go and rust, ..., typing, ... You would expect most software stack to be pretty solid and safe compare to 15 years ago.
This mitigates things to a great extent.
I do not know who thought that having your dependencies depend on the internet with a zillion users doing stuff to each package was a good idea for enterprise environments...
It is crazy how much things can get endangered this way.
The other one a few days ago was also good: https://nesbitt.io/2026/02/03/incident-report-cve-2024-yikes...
In fact, pip is much more dangerous than npm because it lacks a lockfile. uv fixes that, but adoption is proceeding at a snailβs pace.
> residents of the Node.js ecosystem stood unified in their belief that the malicious remote-code execution was a completely unpredictable tragedy
Does anyone believe that claim? There's been so many counterexamples.
It's a great dig on the ecosystem's failings but only entertainment. Perhaps a prompt for marketers to present their wares? Kinda like the maintainer of depsguard who removed, re-added, and then re-removed that admission from their post? At the time of this writing they have the top post.
https://xeiaso.net/shitposts/no-way-to-prevent-this/CVE-2024...
RubyGems: https://www.sonatype.com/blog/anatomy-of-the-rubygems-rest-c... PyPi: literally the latest attack included publishing malicious packages on PyPi XZ Tools, a part of nearly every Linux distribution nearly merged in code to backdoor SSH: https://www.akamai.com/blog/security-research/critical-linux...
It is just easy pickings to blame npm specifically. Yes, while they do share some part of the blame, no package manager is immune from attack and certainly not ones where the attackers exploited being able to extract out secrets from a developer's environment variables or files. Seems more like developers should be managing their secrets better?
I also find that using the meme that this title snowclones is in bad taste too.
After thinking more while typing this:
I think I'd agree we should indeed have a 10-day cooldown (i.e. don't install anything released in the last ten days.) I suppose I just don't think anyone should expect it to be the only mitigation.
The only issue I see is responding to vulnerabilities, where you want to upgrade immediately. But I think in that case it's fine to require the developer to be explicit in the new version they want.
> Disclaimer: I maintain depsguard
Teams should be able to say "at least N developers have to agree to a release before it happens." This should be a policy they can control and lock down with a non developer account.
But won't more people on cooldown mean less likelihood to catch the bug, thus extending the need for cooldowns?
Something like a proxy that intercepts and depending on the source, is intelligent enough to examine the package for age. That would be cool. Already sounds like a cloud product you could sell.
So at the very least, adding a cooldown raises the difficulty of these attacks above that threshold.
Different order of magnitude effort spent during XZ attack.
Also, most malicious versions seem to be detected by tools scanning new packages. People updating without cooldowns probably aren't manualy inspecting diffs. Giving tools more time to detect things seems pretty obviously good to me. Add to that maintainers reporting they've been pwned, and the floor for sneaking malicious code is much higher.
I added a βhow to bypass if you have to patch a zero day CVEβ section to depsguard for all supported package managers.
Edit: added it back, inline.
So add it at the package manager level instead of the user level then?
I think that npm can have its own cooldown and automated security scan. Socket.dev, StepSecurity both close a gap here by spending tokens to scan new popular packages. Whether they do it for marketing or out of the goodness of their heart, is irrelevant. They donβt charge for this service, and itβs something Iβd expect Microsoft (who owns GitHub who owns npm) to do.
(well maybe that leads to kidnappings idk)
edit - heh, sibling comment on package manager-level must be much smarter
This is definitely going to affect any packages that need to link to native code and/or compile shims, but these are very few.
https://pip.pypa.io/en/stable/cli/pip_lock/
But who cares about pip, uv is here.
[0]: https://en.wikipedia.org/wiki/%27No_Way_to_Prevent_This,%27_...
But so is the regular code in those packages! It won't run at install time, but something in there will run -- otherwise it wouldn't have been included in the dependencies.
Thinking that eliminating post-install scripts will have more than a momentary impact on exploitation rates is a sign of not thinking the issue through. Unfortunately the issue is much more nuanced than TFA implies -- it's not at all a case of "Let's just stop putting the wings-fall-off button next to the light switch", it's that the thing we want to prevent (other people's bad code running on our box) cannot be distinguished from the thing we want (other people's good code running on our box) without a whole lot of painstaking manual effort, and avoiding painstaking manual effort is the only reason we even consider running other people's code in the first place.
NPM's achilles is the pre/postinstall step which can run arbitrary commands and shell scripts without the user having any way to intervene.
Dependencies must be run in isolated chroot sandboxes or better, inside containers. That would be the only way to mitigate this problem, as the filesystem of the operating system must be separated from the filesystem of the development workflow.
On top of that most host based firewalls are per-binary instead of per-cmdline. That leads to the warnings and rules relying on that e.g. "python" or "nodejs" getting network access allowlisted, instead of say "nodejs myworm.js". So firewalls in general are pretty useless against this type of malware.
Attackers go where the victims are. Frontend is a monoculture with the vast majority using NPM; backend, less so. This isn't an excuse for NPM, but another strike against it.
You could also argue that the attacks make a deeper point about frontend vs backend devs, but I won't go there.
With many other languages, you have a lot of functionality out of the box. Certainly, there have been bugs and security issues, but they're a drop in the bucket compared to what you see in the JS ecosystem. With other languages, you have a much smaller external dependency graph and the core functionality is coming from a trusted 3rd party.
Maven Central exists for decades the amount of incidents of people stealing namespaces is minimal.
One can't simply publish a package under the groupId "com.ycombinator" without having some way to verify that they own the domain ycombinator.com. Then, once a package is published, it is 100% immutable, even if it has malicious code in it. Certainly, that library is flagged everywhere as vulnerable.
It baffles me that NPM for so long couldn't replicate the same guardrails as Maven Central.
Something fascinating about the design and architecture of programming languages and their surrounding ecosystems is the enormous leverage that they provide to the "core team":
For every 1 core language developer[1]...
... there may be 1,000 popular package developers...
... for which there may be 1,000,000 developers writing software...
... for over 1,000,000,000 users.
This means that for every corner that is cut at the top of that pyramid, the harms are massively magnified at the lower tiers. A security vulnerability in a "top one thousand" package like log4j can cause billions of dollars in economic damage, man-centuries of remediation effort, etc.
However, bizarrely, the funding at the top two levels is essentially a pittance! Most such projects are charities, begging for spare change with hat in hand on a street corner. Some of the most used libraries are often volunteer efforts, despite powering global e-commerce! cough-OpenSSL-cough.
The result is that the people most empowered to fix the issues are the least funded to do so.
This is why NPM, Crates.io, etc... flatly refuse to do even the most basic security checks like adding namespaces and verifying the identity of major publishers like Google, Microsoft, and the like.
That's a non-zero amount of effort, and no matter how trivial to implement technically or how cheap to police, it would likely blow their tiny budget of unreliable donations.
The exceptions to this rule are package managers with robust financial backing, such as NuGet, which gets reliable funding from Microsoft and supports their internal (for-profit!) workflows almost as much as it does external "free" users.
"Free and open" is wonderful and all, but you get what you pay for.
[1] Most of us can name them off the top of our heads: Guido van Rossum, Larry Wall, Kerningham & Richie, etc.
Itβs a bitter pill that we collectively donβt want to swallow, because it has a lot of negative connotations on our ability to deliver individual impact quickly.
People can still bypass these measures if they're determined enough (offline package installs, vendoring dependencies, etc.) but making circumvention impossible to do accidentally and inconvenient to do deliberately solves the problem 99% of the way.
Postinstall scripts run at install time, with installer's privileges.
We all need to slow down and get some perspective. βProgressβ doesnβt mean βrush everything and do it now now nowβ. Advancements should be slow, methodical, considered. Thatβs a good thing, not a weakness.
It's inevitable that a false negative will slip through one day, and when that happens, it will compromise everyone who installs it, no matter if on day one or day eight.
https://en.wikipedia.org/wiki/%27No_Way_to_Prevent_This,%27_...
In JS world there is plenty of competition for package managers pnpm/ yarn/ burn all viable alternatives to npm the package manager.
Public registries for languages tend to coalesce around one service . Nobody wants to publish their library to 4 different registries .
Its childish to believe that because you can't fix everything you shouldn't fix anything. Defense in depth.
I shouldnβt have removed it in the first place, and I re-added it right when someone called me out on it, and explained why I did it (https://news.ycombinator.com/item?id=48156765). But again, itβs not like itβs hidden, itβs right in my bio. But letβs say I didnβt add a disclaimer in the first place, and didnβt even disclose it in my bio, would it matter much?
If the maintainer of pnpm would link to the first link I shared, would i care if they disclose it or not? If it works, it works. Itβs a free tool. I know depsguard is not pnpm, and I do think disclosing βplugsβ is the ethical thing to do, and thatβs why I re-added it, but I donβt understand the obsession with disclosures on free and open source tools that are helping people. You get hated if you disclose it (people hate plugsβ¦) and hated if you donβt.
Again I understand the criticism, but Iβm not a marketer, Iβm someone who is tired of everyone posting on these attacks on LinkedIn for marketing (yes including me), and decided to do something about it (I put my company logo as I used my company laptop and resources) If the tool works, is free, and asks for nothing in return, is clear in my bio that Iβm the maintainer, has now a clear disclaimer (inline) is it really that bad of a crime to build something useful for free and link to it in 2026?
Maven doesn't have "preinstall, install, post install", or " build.rs" for rust, executing arbitrary code during the installation.
The code that's executing with Maven is in your pom.xml, not some hidden code from a transient dependency.
That alone is a major design flaw in both npm and cargo.
Java is boring, because it works. People don't like boring stuff. It's more exciting to play the Russian roulette on each install!
The issue isnβt that the functionality doesnβt exist, itβs always backwards compatibility with versions where it did not yet exist.
Both the Browser and Node.js standard library are fairly extensive. I don't think there's much you can do with other language you can't do with Node.js. And as a lot of newer languages have demonstrated (like zig and hare), you don't need an extensive one.
And that number tends to reduce even more when the ecosystem matures.
In addition, crates.io has not flatly refused to support namespaces, there's an entire accepted RFC for it: https://github.com/rust-lang/rfcs/pull/3243
At the same time, note that namespacing does nothing to prevent any sort of problem here. Namespacing is great for package organization and making provenance more deliberately obvious, but beyond that it's not a security measure.
I like it. Well, it would be tough for everybody whenever a long-awaited feature arrived but was out of touch just behind the glass. Maybe will improve our delayed gratification appreciation!
Dev enforces cooldown on users, not users deciding they want to be safer. Dev has extra step of ensuring they check their accounts every ~23hr indefinitely.
The simple cooldown scenario sees potentially thousands of downloads of a malicious package. The 24 hour developer delay scenario sees zero downloads during the same period.

SAN FRANCISCO, CA - In the wake of a devastating supply chain attack in the npm registry that left millions of enterprise applications compromised and billions of user records exposed, developers across the JavaScript ecosystem expressed deep sorrow today, lamenting that such a crisis was completely unavoidable.
βItβs a shame, but what can you do? This is just the price of building modern web apps,β said Senior Frontend Engineer Mark Vance, echoing the sentiments of a community that completely relies on a 40-level-deep nested tree of unvetted packages maintained by pseudonymous strangers to capitalize a single string. βThereβs absolutely no way to foresee or prevent someone from taking over a long-abandoned utility package and injecting a crypto-miner into every production build in the world. Itβs just an act of nature.β
At press time, residents of the Node.js ecosystem stood unified in their belief that the malicious remote-code execution was a completely unpredictable tragedy, offering their thoughts and prayers to the DevOps teams currently scrambling to rotate their corporate AWS keys.
Interestingly, developers in ecosystems like Go, Rust, and those utilizing native Web APIsβwhere robust standard libraries drastically reduce reliance on third-party code and strict cryptographic verification is built into the core toolchainβreported zero instances of a college dropoutβs weekend project wiping out global logistics infrastructure today.
βItβs devastating, but we have to accept that we live in a world where bad actors exist. There are no registry policies or build-sandbox guardrails we could possibly enforce to stop it,β said an npm spokesperson, standing in front of an open-source registry that happily executes arbitrary installation scripts on local machines by default. βOur hearts go out to the victims. Until the next inevitable breach tomorrow morning, we must simply remain resilient.β
Code runs automatically on import, you don't have to call dependency.infectMePlease()
Your code imports depA which imports depB which imports depC which imports depD which has been compromised, and boom, malicious code runs before you've even finished resolving the imports.
> your CI likely has NPM push tokens, which is how this single-package worm become a multi-package self-replicating worm
I've never once seen or worked with a CI pipeline that ran "npm install" that would be any safer if post-install scripts didn't exist. They all run "npm run test" or similar.
How often do you run "npm install" just for the fun of it, without actively working on the codebase?
IME 99% of the time the time between "npm install" and some form of execution that pulls in dependencies is less than 30 seconds.
NIH nihilism
You don't need to test a compromised package to have it execute code. Importing it anywhere in your tests is enough, even transitively.
It's for sure less likely to run but I doubt it's significantly different in practice.
Your mismatch is that you think in policies, not assessments here. Nothing in my normal go workflow will ask me if I want to run "curl download whatever from the internet" when I run go build.
Though I agree with the difference in workflow, there is not a single mechanism in go catching this. go.mod files can be just patched by the worm, and/or hidden behind a /v123 folder or whatever to play shenanigans on API differences.
I did not miss that.
The "culture" of NPM was firmly established long before the acquisition by Microsoft.
Similarly, there clearly isn't the same feeling of "ownership" over NPM and its giant pile of anonymously published packages as there is over NuGet where a substantial fraction of the traffic is Microsoft customers downloading Microsoft packages for Microsoft DotNet development on Microsoft Visual Studio for Microsoft Windows Server.
I just hope that the companies who currently perform security scans for free/for exposure have a sustainable business model. Once such a company gains reputation, there's diminishing returns in headlines currency.
FWIW, if anything, my invalid criticism was only a footnote to my comment. I was irritated by the article. It felt like a low blow on a dynamic that is a terrible, if minor, example of how small but impactful set of people are shitty to others in the world. Their accumulated behaviors mean we don't have nice things and we get stuck on defense rather than dreaming and creativity. It went further and made universal false claims about the users of npm and what we say and believe. Impugning you was just a mistaken tack on. Again, I apologize. The article hit some sore spots and it sucks that I added to the pile of crappiness in the world. I try hard not to and to improve things instead.
No, it's certainly not a crime to promote your project. Especially if it's free and open source and you won't be attempting to bleed your adopters in the future. Promoting your tool is marketing IMO, even if not financially motivated, and regardless of your job title. I'm going to just avoid the scree I'm thinking about with regards to open source and it's role in the industry that I am more and more distant from anymore.
Would it matter if you hadn't disclosed? I think it's complicated. I'm definitely from the camp that thinks one should. Being an expert on the thing, likely having better insight in the space, and the bias inherent therein are important signals. I always disclosed on comments about my own projects though I don't list them in my bio. They're mostly outdated these days. Of course you get your own opinion and should act accordingly.
Examples that come to mind: webview/webview, webkit, cilium/ebpf and most other CGo projects that I have seen.
I'm not saying that npm is doing everything right, but I suspect that beyond the obvious low-hanging fruit that we hear about pretty consistently with npm there's probably a long tail of less obvious stuff that can be exploited that will not be specific to npm. The fundamental problems with supply-chain vulnerabilities aren't going to go away if npm magically became pip or go modules overnight.
Especially the number of times I had to clean all the caches in order for maven and gradle to build the project is just far too high for me. It shouldnβt ever be needed if an ecosystem is meant to be considered boring. I feel like Java doesnβt build when I look at it wrong.
But in the Maven/Gradle ecosystem, most projects pin exact dependency versions. Support for version ranges and dynamic versions exist, but they are generally avoided because they hurt reproducible builds. That means a malicious new release does not automatically flow into most consumersβ builds just because it was published.
I'd go as far to say that NPM should:
1. Enforce scope (namespace) requirement, and require external verification (reverse DNS for example).
2. Disable version range support out of the box. User must --enable this setting from the command line at all times.
3. Remove support for install scripts completely. If someone wants to publish a ready-to-run software, there are plenty of other mechanisms.
https://www.stepsecurity.io/blog/mini-shai-hulud-is-back-a-s...
- editing comments more transparently
- err on always disclosing vs not
Thanks again for the reply. <3
And IMO the complaints about Python packaging tooling are overblown. Setuptools on its own was a bit disappointing, but coming from PHP 20 years ago it was a revelation! Virtualenvs and requirements.txt were an further improvement and so was pip β in an era where most other scripting languages didnβt have pinning for sub dependencies either; but you could always βpip freezeβ to capture everything.
Later on, pipenv wasnβt perfect, but it was enough. I never ran into any of the headaches people keep saying poetry and uv solve. Poetry on the other hand always gives me one reason or another to beat my head against a wall.
That said, Iβve never bothered to try to publish anything and canβt comment on that end of it.
Python is the antistandard for package management. Or maybe even the eldritch horror of package management.
<plugin>
<artifactId>exec-maven-plugin</artifactId>
<version>3.5.1</version>
<groupId>org.codehaus.mojo</groupId>
<executions>
<execution>
<id>Generate-shared-lib</id>
<phase>package</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>bash</executable>
<arguments>
<argument>generate-lib.sh</argument>
</arguments>
<environmentVariables>
<JAVA_HOME>${env.JAVA_HOME}</JAVA_HOME>
</environmentVariables>
</configuration>
</execution>
</executions>
</plugin>
At least with certain plug-ins Maven will execute arbitrary commands at build time. And if you need that to build native bindings it feels like a big hole. Granted, most projects don't need JNI, I guess.Hah, too true! I guess it is boring in the fact that it is not as... move fast and break things... as NPM. But Java build systems are still certainly fun and challenging in their own ways.
But also, almost all of those deps on all simple apps are the same in Rust. They are the same for a large part in JS too, but it's for a smaller part than on most languages.
Like you have axios.js that decides in turn to depends on the "follow-redirects" library. IMO, the best move would be for axios to vendor the code. Same with "proxy-from-env" Just tiny libraries scattered all over the web. Something like axios, should purely depends on the runtime library.
However the current npm vulns used a post install script.
Who the heck says everyone who publishes a library has a domain? That seems absurd.
This directly influences how well e.g. version pinning works. In the Java world, package versions are _relatively_ independent from eachother and have few transitive dependencies, and as such version conflicts are relatively rare. This means you can get away with full pinning of all dependencies, with the occasional manual override of a conflicting transitive dependency.
This doesn't work in JavaScript. The dependency ecosystem is massively intertwined, if every library would specify exact versions you'd end up with literally hundreds of conflicts to resolve. That's not feasible. As a result, they've chosen the middle ground of using lock files in addition to version ranges.
This also hurts the effectiveness of verified namespaces: when packages come from hundreds of different sources, you're not going to notice 1 or 2 sketchy ones in there.
Other consequences of the big monolithic packages in Java are that updates tend to be less frequent, and more often from large reputable venders. Both of these help to reduce the problem too.
While the JavaScript toolchain can definitely learn a lot from the Java toolchains, the problems it needs to solve are not the same, and thus solutions don't translate 1-1.
At least I hope that they'll get rid of install scripts, that's such a low hanging fruit that really should've be done a decade ago.
Post-install scripts have a slight edge over executing malicious code on import, i.e. they work 99.95% of the time instead of 99.9% of the time, but removing these scripts wouldn't materially change the situation we're in. You're locking the back door but leaving the front door and all of the windows wide open.
I'm going to suggest that we might be worse off in the short-medium term if post-install scripts are removed because everyone who thought that disabling post-install scripts was a "good enough" standalone security strategy will get caught with their pants down as attackers modify their payloads.
This exists because domains (historically) used to be expensive by western standards. .com used to be $75/year back in the day.
How will that help? It's just going to break things that legitimately require them.
Instead of being infected upon running "npm install", you'll just get infected upon running "npm run" instead. The former is slightly more reliable but fixing that is just kicking the can down the road. Maybe we'll have a few days before the payloads get rewritten.
The "instead of" depends very much on the exploit and where it's wedged in the code. I doubt it's anywhere near 99%. Plus, getting the exploit to execute on the developer's machine is difficult to manage even in the best cases.
> because everyone who thought that disabling post-install scripts was a "good enough" standalone security strategy will get caught with their pants down as attackers modify their payloads.
Saying "well there are stupid people in the world" seems like a pretty bad justification to leave a hole open.
Don't count other people money.
We don't need to guess, it's going to be wedged in index.js, probably on line 1.
Are you aware that all transitive dependencies are executed immediately? You depend on PackageA which imports PackageB, which imports PackageC, which imports a trojanized PackageD. As soon as PackageD is imported, it executes its payload and infects your machine.
All of this happens in a blink of an eye, as soon as you run anything that kicks off an import chain containing a trojanized dependency.
Try it for yourself. This will simulate a malicious transitive dependency: koa > cookies > keygrip > tsscmp. You don't need to do anything except import koa.
mkdir demo && cd demo
npm install --save koa@3.2.0
echo 'import "koa";' > demo.mjs
echo 'console.log("\n\n--- pwned by a transitive dependency ---")' >> node_modules/tsscmp/lib/index.js
node demo.mjs
> Saying "well there are stupid people in the world" seems like a pretty bad justification to leave a hole openThen you're calling much of the HN audience stupid. I've had this argument on here several times - and this is the top percentile of people who try to do something at all.
The justification for leaving this hole open is that it's a waste of time, resources, and mindshare patching a hole when there's a comparable and unpatchable hole right next to it. Advocate for things that actually work, like sandboxes.
A small bar of $20/year is also enough to completely cut-down on contributors who sign up with the intention of publishing malicious packages: they have to pay $20/year for each malicious package they want to publish!
Heck domain names are ephemeral, forget a deadline by a day and they are snatched up my squatters. They don't provide any extra guarantees. Do we really think a domain requirement is going to stop state level actors that are already stealing 2FA package publishing tokens from major software orgs?
Is that your target? Because if so, then nothing will stop them.
Requiring domain name verification is not going to do anything when 2FA tokens are being stolen.
What it will do is prevent students and people who want to stay anonymous from contributing to open source.