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)
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.
I added a “how to bypass if you have to patch a zero day CVE” section to depsguard for all supported package managers.
So at the very least, adding a cooldown raises the difficulty of these attacks above that threshold.
> Disclaimer: I maintain depsguard
Edit: added it back, inline.
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.
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.
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.
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.
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.
If every other week I would notice the FDA recalls a popular brand that would have taken over my brain and transmit my bank password and SSN to a stranger, I might prefer drinking week old milk.
Edit: not dismissing your analogy, it’s pretty much it.
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.
https://pip.pypa.io/en/stable/cli/pip_lock/
But who cares about pip, uv is here.
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 .
https://xeiaso.net/shitposts/no-way-to-prevent-this/CVE-2024...
[0]: https://en.wikipedia.org/wiki/%27No_Way_to_Prevent_This,%27_...
https://en.wikipedia.org/wiki/%27No_Way_to_Prevent_This,%27_...
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.
The other one a few days ago was also good: https://nesbitt.io/2026/02/03/incident-report-cve-2024-yikes...
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.
> 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.
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.
Different order of magnitude effort spent during XZ attack.
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.
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!
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.
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.
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.
And that number tends to reduce even more when the ecosystem matures.
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.
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.
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.
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.
Postinstall scripts run at install time, with installer's privileges.
Its childish to believe that because you can't fix everything you shouldn't fix anything. Defense in depth.
This is definitely going to affect any packages that need to link to native code and/or compile shims, but these are very few.
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?
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.
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.
NIH nihilism
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.
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.
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.
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.
- editing comments more transparently
- err on always disclosing vs not
Thanks again for the reply. <3
I do think cooldowns help, it’s more that this analogy doesn’t help.
The cow has to wake up and look at what milk she’s been putting out, and ideally the milk machine would use an early release channel so that some people will get the brain virus first.
The early release channel is sensible, but if you're a bad actor who's compromised a package you're not going to early release are you, you get it straight out there.
tl;dr - there is financial interest for some companies to not have cooldowns and detect poisoned milk immediately.
More accurate analogy is a food tester for a king I guess, if your replace king with "everyone", (and the food tester did it for inbound traffic for his product and didn't risk their life for it)
The crazy thing is the risk from food is higher, we just don't really mind, because it's rare that we personally get affected
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.
Examples that come to mind: webview/webview, webkit, cilium/ebpf and most other CGo projects that I have seen.
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.
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.
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.
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.
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.
<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.However the current npm vulns used a post install script.
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.
Python is the antistandard for package management. Or maybe even the eldritch horror of package management.
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.
https://www.stepsecurity.io/blog/mini-shai-hulud-is-back-a-s...
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.
So add it at the package manager level instead of the user level then?
(well maybe that leads to kidnappings idk)
edit - heh, sibling comment on package manager-level must be much smarter
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.
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.
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.
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.
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.
Who the heck says everyone who publishes a library has a domain? That seems absurd.
This exists because domains (historically) used to be expensive by western standards. .com used to be $75/year back in the day.
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.
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.
Don't count other people money.
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.