7 ms·
Show HN: Safe-NPM – only install packages that are +90 days old
This past quarter has been awash with sophisticated npm supply chain attacks like [Shai-Hulud](https://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem https://www.cisa.gov/news-events/alerts/2025/09/23/widesprea...() and the [Chalk/debug Compromise](https://www.wiz.io/blog/widespread-npm-supply-chain-attack-breaking-down-impact-scope-across-debug-chalk https://www.wiz.io/blog/widespread-npm-supply-chain-attack-b...). This CLI helps protect users from recently compromised packages by only downloading packages that have been public for a while (default is 90 days or older).
Install: npm install -g @dendronhq/safe-npm
Usage: safe-npm install react@^18 lodash
How it works:
- Queries npm registry for all versions matching your semver range
- Filters out anything published in the last 90 days
- Installs the newest "aged" version
Limitations:
- Won't protect against packages malicious from day one
- Doesn't control transitive dependencies (yet - looking into overrides)
- Delays access to legitimate new features
This is meant as a 80/20 measure against recently compromised NPM packages and is not a silver bullet. Please give it a try and let me know if you have feedback.
- pr0xyb0i 10mo agoHow does it differ from: https://docs.npmjs.com/cli/v11/commands/npm-install#before https://docs.npmjs.com/cli/v11/commands/npm-install#before
- silverwind 10mo agoSeems like a worse version of `before` because `before` also handles indirect dependencies, whil this module does not seem to.
- asdkkthrowaway 10mo agoDoesn't this just mean you're 90 days late on any patches?
- beepbooptheory 10mo agoThis article was on the front page recently that discusses the idea behind this: https://blog.yossarian.net/2025/11/21/We-should-all-be-using-dependency-cooldowns https://blog.yossarian.net/2025/11/21/We-should-all-be-using... Most of the time, you need quick patches because of fairly recent dependency changes, so if you just wait and kind of "debounce" you dependency updates, you can cover a lot of supply chain vulnerabilities etc.
- ntonozzi 10mo agoIt's not debouncing, it's delaying. Ideally you can still update a specific dependency to a more up to date version if it turns out an old version has a vulnerability.
- moritzwarhier 10mo agoauto-updating is bad. Scheduled, audited updates are good. Installing random npm packages as suggested here is also bad. Especially with "--global", although I'm not sure if that makes any difference because Node by default of course can access all of your file system.
- someothherguyy 10mo agohttps://pnpm.io/supply-chain-security https://pnpm.io/supply-chain-security
- tkzed49 10mo agoNot controlling transitive deps makes this vastly less useful because direct deps can specify version ranges (e.g. latest minor version). Personally I'd stick with pnpm's feature.
- zelphirkalt 10mo agoThis is why one should pin all direct and transitive dependencies with their checksums and not upgrade everyday willy-nilly. There is no need to specify the specific version numbers of transitive dependencies, if one keeps a lock file that pins those exact versions and checksums of transitive dependencies, and one doesn't upgrade willy-nilly all the time. Make upgrading dependencies a conscious choice, and perhaps have a policy of at most upgrading every X days.
- tkzed49 10mo agoI don't think it's accurate to envision that the average team using the npm ecosystem is upgrading their dependencies daily. Rather, the problem is that modifying your direct deps (e.g. adding a package, upgrading a package) requires modifying transitive deps. So yeah, ~everyone is using a lockfile with checksums. But even if I think really hard about installing XYZ@1.2.3 package, and check that the lockfile diff is reasonable, I'm not manually auditing the whole supply chain (I'd get fired for getting nothing done). And a single dependency change that I choose to make can affect a substantial number of transitive deps.
- zelphirkalt 10mo agoMy idea is, that they do _not_ upgrade their dependencies daily, because that is what is causing the issue. People don't pin all their versions and checksums properly, and the next time they run `npm install` they get a new version of some library. I don't even want to see any "@^1.2" or whatever the syntax was. Also they should be running `npm ci`. I have seen this multiple times with people from various backgrounds and in frontend as well as backend. People still think like "Lets auto upgrade patch releases, so that we always get the bugfixes." or "Lets upgrade quickly, so that we deal with changes right away, before accumulating the work.". But they don't think properly about security and reproducibility.
- moritzwarhier 10mo agoAs someotherguyy already mentioned, this is a default feature in pnpm. And as far as cat-and-mouse-games go in other package managers, I'd say that pinning dependencies and disabling postinstall scripts is a much better option. Sure, not a foolproof one either, but as good as it gets. edit: misspelled someotherguyy's user name
- 2muchcoffeeman 10mo agoWhy is the community persisting with such poor solutions?
- moritzwarhier 10mo agoWhat would be a better solution? Do other package managers reliably restrict access to the host system beyond the scope of the project folder? Many quirks come from abilities that were once deemed useful, such as compiling code in other languages after package install. Sure, today, I can disable install scripts if I want but it doesn't change much when I eventually run code from the package anyway. But even restricting access to the file system to the project's root folder would leave many doors open, with or without foreign languages: Node is designed as a general purpose JS runtime, including server-side and build-time usage. The utility of node.js was initially to provide a JS API that, unlike the web platform, is not sandboxed. And npm is the default package manager. This not only allows server-side usage, but also is essential to many early dev scenarios. Back in the days, it might have been SCSS builds using node-gyp (wouldn't recommend). Today it's things like Golang TypeScript or SSGs. So, long story short: as many people before me already said, it's an ecosystem/cultural problem. One thing against npm in this regard was/is its broken lock-file handling until I think version 12 or 16. That led to unintended transitive dependency version changes, breaking any reproducibility. Same for compiling foreign languages. These problems are solved today / not different from other package managers and -registries, as far as I know. The culture of taking breaking changes and dependency bloat lightly has not changed as much, I think, although it's improved. This most important point seems to be related to 3 reasons IMO: - junior developers without experience in library development reaching large audiences - specs, languages, runtime, and the package managers itself going through disruptions and evolutions - rapidly releasing breaking majors, often caused by the above factors The combination of these plus the role of the project lead/team who actually decides about the dependencies. There are probably also many projects with unclear roles and many people who can push manifest changes, coupled with habitual access to CI/CD pipelines.
- mrconter11 10mo agoBut safe-npm is not 90 days old yet.. :/
- jagged-chisel 10mo agoConsider this a 3-month lead on the ability to utilize it
- robkop 10mo agoYou could dual brand as vibe-npm, only install packages that are in your models training dataset
- cheesekunator 10mo agoWhy does elapsed time mean a library is safe? This is so ridiculous. It doesn't protect you against anything. I'm sure there are 1000s of old libraries out there with hidden vulnerabilities or malicious code.
- femiagbabiaka 10mo agoMost supply chain attacks have a very limited window in which they’re exploitable. This is not a panacea, but it is a good idea.
- deleted 10mo ago[deleted]
- Waterluvian 10mo agoLiterally nothing can mean a “library is safe.” The idea of “safe” in terms of risk and security has misled a lot of people into this wrong idea that there’s a binary state of safe and unsafe. It’s all about risk management. You want to reduce risk as inexpensively as possible. One of many inexpensive approaches is “don’t install dependencies that are new.” Along with “don’t install dependencies that nobody else uses.” You might also apply the rule, “don’t install dependencies that aren't shipped with the OS.” Or “don’t use dependencies that haven’t been formally proven.” Etc. Indeed, calling it “Safe-NPM” can be misleading. As if using it achieves some binary state of safety.
- PunchyHamster 10mo agohedging bets of zero day vs compromise (that have big chance to be found in thos e 90 days). But yeah, not a good idea
- ttoinou 10mo agoIf everybody does that, won't we take 90 days more to detect problems / hacks of npm packages ?
- lelandbatey 10mo agoNo, cause the folks detecting the problems typically do so by actively scanning new releases (usually security companies do this). Few such problems are detected by people who do a "normal" update and receive compromised code, investigate, and then report the problem. It does happen, but it's not the "usual" way these supply chain attacks are discovered, especially not the really big ones.
- deleted 10mo ago[deleted]
- mikeweiss 10mo agoUmm... Tell me how the most recent supply chain attack was discovered again?
- codezero 10mo agoDoes anyone have any statistics on how long a compromised package has been in the wild on average?
- sebmellen 10mo ago"Here, install my new 1-day old NPM package that doesn't let you install packages younger than 90 days." Pardon me, I couldn’t help myself :D
- MuffinFlavored 10mo agoremindme! 89 days
- h33t-l4x0r 10mo agoYou would `npm link` that thing in real life I think.
- mort96 10mo agoI get that it's a joke, but I feel the need to defend this project anyway. The problem with NPM isn't any one young package. The problem with the NPM is that any time you run 'npm install', you download potentially thousands of packages, and you get the most recent patch release from all of them. Installing one 1-day-old NPM package to forever avoid day 1 releases of thousands of packages seems like a worthwhile trade. Still, I would maybe choose the tried and true PNPM instead, which supports this too.
- moritzwarhier 10mo ago> The problem with NPM isn't any one young package. The problem with the NPM is that any time you run 'npm install', you download potentially thousands of packages, and you get the most recent patch release from all of them. Isn't this simply wrong? Last I checked, lock files work. They didn't for a long time, until a couple of years ago, as far as I know. If you delete your lock file or explicitly run a package upgrade, sure, you get the latest versions compatible with your semver ranges. > Installing one 1-day-old NPM package to forever avoid day 1 releases of thousands of packages seems like a worthwhile trade. If you want to be extra sure, you can simply not use semver ranges in your package.json, or only for select packages. As far as I know, this is recommended anyway.
- 10mo ago
- arrty88 10mo agoWith the help of AI, i see no reason to install most deps nowadays besides types and react and mui framework. Everything can be built from scratch quickly.
- zelphirkalt 10mo agoYou still will have to maintain it then though.
- actionfromafar 10mo agoI think this is a pretty common approach nowadays, and one of the reasons why I believe my job is safe for now. I expect to be called up to fix some of the resulting mess. It's a two-edged sword, for sure.
- catlifeonmars 10mo agoNow you have shifted your supply chain issues to your coding agent.
- solumunus 10mo agoAnd do you think the severity of the issue is anywhere near the same?
- zelphirkalt 10mo agoI think this will remain to be seen. Wasn't there a paper linked here on HN recently, that claimed, that even few examples are sufficient, to poison LLMs? (I didn't read that paper, and merely interpreted the meaning of the title.)
- solumunus 10mo agoI don't think it remains to be seen. I think it's obvious that the completely explicit exploit is going to be more effective.
- deleted 10mo ago[deleted]
- zelphirkalt 10mo agoThis works only if there are some other people, who will use a dependency "too early" to fall victim to some exploit and then notice it, within those 90 days. Imagine, if everyone only used packages older than 90 days. Then we would have no frontrunner to run into the issues before us. A cooldown time alone is not actually a sufficient solution. What people really need to stop doing, is not properly pinning their versions and checksums, and installing whatever newer version is available. That would cause a problem even, if the date line is moved 90 days into the future for all packages. If however, one only updates versions of dependencies when one consciously makes that choice, there are far fewer points in time, when versions change, and therefore the chance of catching something is also much lower. Combine that with a cooldown time/minimum age for versions, and you got an approach.
- LelouBil 10mo agoYes and no, usually when malicious packages go public it's some third party cybersecurity firm that scans packages that found it.
- waterTanuki 10mo agoJust use pnpm. I've never once had compatibility issues with it on linux/mac/windows over the past 6 years.
- aussieguy1234 10mo agoThis does mean that security patches released yesterday won't get installed. Its the opposite of "keep your software up to date"
- deleted 10mo ago[deleted]
- PunchyHamster 10mo agoNow with 9000% more zero-days! > Installs the newest "aged" version Probably want to install version that has CVE-fixed instead, i.e find the cve for packages and install latest version that has all of them fixed but not later. Technically someone could fake a cve to get people to upgrade but that's a far more involved process
- spullara 10mo agothis is a good idea but i just did this: alias npm='npm --before="$(date -v-1w +%Y-%m-%d)"' alias pnpm='pnpm --before="$(date -v-1w +%Y-%m-%d)"'
- m4rtink 10mo agoSo how is not using those Debian packages because they are too old working out ? ;-)
- ghtbircshotbe 10mo agoRecently decided to learn typescript. You would hardly know there's a Debian package from reading typescript's website.
- c-hendricks 10mo agoWhy would the typescript webpage mention Debian at all?
- ghtbircshotbe 10mo agoIn telling you how to install it. That's kind of the point - they all assume you're going to use npm to install it.
- c-hendricks 10mo agoDoesn't seem very far fetched to use a node package manager to install a node package tho?
- ghtbircshotbe 10mo agoMaybe not. But I'm not going to if I can help it, which is entirely doable by using Debian's package manager. And I avoid running things as root if I can help it. It's bad enough I need to run apt as root. The recent recent news hasn't increased my trust in npm.
- m4rtink 10mo agoUpstreams often mention distros in which their software is packaged, how the package is called and sometimes even the commands to install it.
- yokto 10mo agoYarn has a configurable npmMinimalAgeGate setting as well since version 4.10: https://yarnpkg.com/configuration/yarnrc#npmMinimalAgeGate https://yarnpkg.com/configuration/yarnrc#npmMinimalAgeGate
- vrighter 10mo agothe more people use this, the less useful it becomes for everyone. If everyone uses this, then everyone would still be using a particular package for the first time at the same time. What then? Release another package that extends the delay to 6 months?
- mdx97 10mo agoMalicious packages aren’t just found because someone gets pwned, there are organizations out there proactively scanning for this stuff.