You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[RRFC] Ability to set minimum maturity (in days) of versions to upgrade #646
When a vulnerability is introduced to an npm package, it takes at least several days to discover the vulnerability and to report the vulnerable release to security databases (npm, Snyk, dependabot, etc.)
Because of that reason, it would make sense for developers who want stability and security for their projects to use 3rd party dependencies with version that matches these conditions:
as latest as possible (the obvious one)
free from vulnerabilities, i.e. not present in npm/Snyk/dependabot security databases
X days mature, because of the reason mentioned above - it takes time to discover and report vulnerabilities
There's existing --before parameter, but it doesn't work for this purpose
Examples
As a maintainer of a popular plugin I several times shipped a bug or a breaking change in a patch release. Usually, these issues are noticed by users during the next several hours and after several more hours, I ship another patch release with a fix.
And here's why --before wouldn't work:
some-cool-package has 6.6.6 with a bug released 4 days ago and 6.6.7 with bug-fix released 2 days ago
with --before="3 days" the buggy version would be installed which would be the exact opposite of desired
with --stabilityDays=3 neither 6.6.6 nor 6.6.7 would be installed which is desired because 6.6.6 is buggy and 6.6.7 is immature.
Reacted by Carlos Hernandez, retrievaaaa, FranSan, Ernesto Ramirez, dloetzke, Renato Granados, Masahiro Saito, Dalibor Novak, Petrus Lundqvist, Will Slattum and 29 more
Reacted by Karl Horky, Ankur K, Kirill Groshkov, Quentin, Olivier Tassinari, leah, Daiki Maekawa, Lucas Santos, Paulo Gonçalves, rubiesonthesky and 26 more
Reacted by Karl Horky, Kirill Groshkov, Steven, Quentin, Shadab Ansari, Bas van den Wollenberg, Renato Granados and Masahiro Saito
waiting a length of time doesn’t increase security, and if such a practice became common then it would just delay discovery of vulnerabilities until after that time anyways
Reacted by yourock1988, Bruno Henriques, Sean Blonien, Julien DEMAZIERE, Paul Douglas Brimicombe and pshryock-nba
i'm a little confused how --before doesn't solve for this.. yes, you have to form the timestamp yourself but it won't install anything that's newer than the date listed, so if you give it a timestamp for 3 days ago that should be pretty close to what you're asking for. at install time we evaluate for and do our best to avoid known vulnerable versions of packages, however if there's no semver-compatible alternative then we don't have much choice but to go ahead and install it.
the second part of that problem will be addressed by audit policies which would be capable of refusing the installation of any package with a known vuln.
I introduced a new proposal that could accommodate a specific metadata field for this purpose (the goal of my proposal is entirely different, but certainly not incompatible).
In the #703 RFC I propose to introduce a new updatePolicy section to provide finer grained control over how we update our dependencies (direct and transitive). We could introduce there a field called minReleaseMaturity that accepted temporal interval values in format ISO 8601; then, if the package manager respects that field, it should never update to a dependency that is not "mature enough".
waiting a length of time doesn’t increase security, and if such a practice became common then it would just delay discovery of vulnerabilities until after that time anyways
In some cases, Attackers may be able to add malicious commits or publish poisoned packages within a short timeframe.
Even if such attacks are easily detected by maintainers, it has been found that they can affect many developers during the several-hour time lag before maintainers can recover appropriately.
While this feature may not be a fundamental solution for attack, it can be expected to act as a fail-safe mechanism that reduces the potential for many developers to be harmed.
pnpm decided to release this feature, probably in response to the lessons learned from Nx. pnpm/pnpm#9921
Having npm itself, which has a larger user base, incorporate this feature would be beneficial for the entire JavaScript ecosystem.
Any comments are welcome.
Reacted by Limon Monte, Kael, Kenta Moriuchi, syumai, Zoltan Kochan, leah, Daiki Maekawa, Daiki Nishikawa, Kenta Kase, Teppei Sato and 24 more
waiting a length of time doesn’t increase security, and if such a practice became common then it would just delay discovery of vulnerabilities until after that time anyways
Socket’s AI-powered scanner quickly flagged these releases, which attempted to steal tokens, SSH keys, and cryptocurrency wallet data and exfiltrate them to public GitHub repositories prefixed with s1ngularity-repository.
So I'd argue there's still a good chance of vulnerability discovery even if the ecosystem moves toward this approach.
waiting a length of time doesn’t increase security, and if such a practice became common then it would just delay discovery of vulnerabilities until after that time anyways
I also agree with @uncenter and this tweet because, as of today, 3 years later, the ecosystem has evolved quite a bit and we manage to pick up these vulnerabilities sooner than before and it would be a nice addition to the package manager if we could have this feature. We also use renovate here and this feature not only helps us to avoid zero day vulns but also allows for a more stable and consistent update of the packages.
For example, if we update a package on the day a new update is out it might contain bugs, if we wait 5 days and then update, these bugs will be more discoverable and fixed. Unless the entire Node.js community decides to use the same lead time to wait, we will still benefit from that. So it's a heavy +1 for me to have it here
But I also agree to this, it's not a package manager problem, it's a repository issue. One that could be mitigated if NPM (the repository), for example, added something like:
4-eyes review of releases for popular packages: If your package has more than X dependents (or downloads, or any relevant metric) you must select another (or more than one) contributor that will need to also approve the next tag releases
Another option but not exclusive would be that, following the same metrics (or even leaving it open as a setting for everyone) you needed to set two (or more) different email addresses in your profile, once a release is made, you need to confirm in all of them. And, in case you need to change any of them, you also need to confirm the change in the other and it should take at least a day for it to be effective (for example, Cipsoft from Tibia does this and works okay for some extent)
The previous could also be extended to MFA, passkeys or other auth methods that are present physically or uniquely with the author.
Are these ultimate solutions? Absolutely not, but they should help mitigate the bulk of supply chain attacks
Reacted by leah, Paulo Gonçalves, Birk Skyum, Daiki Maekawa, Martin Cattroll, Grant Kiely and Ben Jenkins
pnpm decided to release this feature, probably in response to the lessons learned from Nx. pnpm/pnpm#9921
No, pnpm did not release the proposed here feature. They did not implement stabilityDays, but minimumReleaseAge, these 2 are not the same. No lessons are learned from Nx so far, sorry 😄
Here's the quote from my request:
some-cool-package has 6.6.6 with a bug released 4 days ago and 6.6.7 with bug-fix released 2 days ago
with --before="3 days" the buggy version would be installed which would be the exact opposite of desired
with --stabilityDays=3 neither 6.6.6 nor 6.6.7 would be installed which is desired because 6.6.6 is buggy and 6.6.7 is immature.
pnpm's feature minimumReleaseAge=3days would install 6.6.6 which is the worst possible option. It's just something similar to npm's long-existing --before param
Reacted by Bas van den Wollenberg, Shai Coleman, Alistair Smith, Paul Armstrong, Birk Skyum, Stefan van der Walt, Bret Comnes, Chris Adams, leah, Felix Bernhard and 5 more
But Renovate's minimumReleaseAge is not the same as pnpm's minimumReleaseAge. Renovate's minimumReleaseAge is what I name as stabilityDays and pnpm's minimumReleaseAge is basically an alias for the npm's --before param
Reacted by Bas van den Wollenberg, averms, haya14busa, Maja Cieślak, Lucas Santos, Jody Hoon-Starr, Dario Vladović, yourock1988 and d-damien
I have implemented this feature in pnpm and I see two features that are missing on the registry side.
the time field is not included in the abbreviated packument. So, we need to request the full document instead.
when installation is done by a dist-tag (e.g. pnpm add eslint@latest) and the latest version is not mature enough, we do not know what other versions were ever released as "latest". It would be good to store every dist-tag that was used in the past. Alternatively, we could throw an error in this case. For now I just pick the biggest version that has the same major version as the active "latest" and satisfies the time constraint.
Reacted by Theodore Brown, Michael H, Lucas Santos, William Stein, Dario Vladović, Momo Kornher and Tomoya Yamashita
Reacted by Bret Comnes, Valentin Semirulnik, Karl Horky, William Stein, Dario Vladović, Momo Kornher and Tomoya Yamashita
I have implemented this feature in pnpm and I see two features that are missing on the registry side.
the time field is not included in the abbreviated packument. So, we need to request the full document instead.
when installation is done by a dist-tag (e.g. pnpm add eslint@latest) and the latest version is not mature enough, we do not know what other versions were ever released as "latest". It would be good to store every dist-tag that was used in the past. Alternatively, we could throw an error in this case. For now I just pick the biggest version that has the same major version as the active "latest" and satisfies the time constraint.
If I understood correctly, this would require some sort of standardization in release tags won't it? Like, people will need to agree what is a "releasable" version and tag it accordingly, otherwise we won't know which is an actual release. Or we are just assuming every tag should be treated as a dist release?
The behavior of evaluating the age between each release would lead to very unusual, unwanted behavior, in the case of fast-releasing packages.
Eg. it would lead to recursive checks of each version back to the original version and then blocked install of ANY version in such a case:
a reasonably longer --stabilityDays of eg. 7 days (or longer, like 14 days)
a fast-releasing package which never has 7 days between releases
This compounds if --stabilityDays should also be applied to transitive dependencies - which it should, because security vulnerabilities in transitive dependencies are often still relevant. Given --stabilityDays=7 with your current algorithm, that means that all transitive dependency versions (versions of dependencies specified by other dependencies) would also need to satisfy this "7 days stable" range, which would further reduce the installability of any packages.
It could be argued that users "should know" about which dependencies release fast and for them to configure manual exceptions to --stabilityDays, but that puts more pressure on end users to know their entire dependency graph and perform complex configuration.
Probably what is best (outside of a completely new algorithm not yet discussed, see edit below) is to keep it simple like pnpm's minimumReleaseAge algorithm, which counts a negative minimumReleaseAge from the present day instead of between previously released versions. This has also been proposed for npm over here:
Edit: Addon to algorithm proposed by @RiskyMH for Bun:
with a 7 day override where it gives up stability check
So I read this as: only apply @limonte's "stability check" algorithm to versions within max 7 days backwards. This seems to mitigate the concerns with fast-releasing packages, and avoids the recursive check beyond those 7 days.
If it is defaulted (or sufficiently adopted), it will simply delay discovery once attackers adapt, defeating the purpose in the first place
@ljharb This may have been historically true, where end users were the unfortunate ones to discover the vulnerabilities. But given the intense attention of security companies and researchers on attacks right now (Socket finding 1000 attacks a week currently) and the possibilities of future automation (eg. automated security check queuing built into npm), maybe that is shifting?
Security companies and researchers would configure away from the default (down to 0 days) and have a period of one or more days to do their work before the wider ecosystem is affected.
The registry having more integrated, automated security checks, like many have suggested on GitHub and Twitter in the last days, would be of course even better, but seems like that is a structural change which will take longer to push through.
If I understood correctly, this would require some sort of standardization in release tags won't it? Like, people will need to agree what is a "releasable" version and tag it accordingly, otherwise we won't know which is an actual release. Or we are just assuming every tag should be treated as a dist release?
If you check how the node.js index file looks like. It is somewhat similar. Every version of node.js is marked with lts true/false.
If I understood correctly, this would require some sort of standardization in release tags won't it? Like, people will need to agree what is a "releasable" version and tag it accordingly, otherwise we won't know which is an actual release. Or we are just assuming every tag should be treated as a dist release?
If you check how the node.js index file looks like. It is somewhat similar. Every version of node.js is marked with lts true/false.
If it is defaulted (or sufficiently adopted), it will simply delay discovery once attackers adapt, defeating the purpose in the first place.
This is not true, because it is possible to discover these things without anyone installing them. Besides the mentioned security scans, many attacks take the form of account takeovers where the account holder is likely to notice fairly quickly. Not all malicious packages would identified in time, but many would, and security is not a binary.
However, even if this were true, the feature being on by default would still have the advantage of adding a delay to the speed with which worms can spread in the registry. Right now developer A can be compromised and have their packages poisoned, then developer B who uses A's packages is compromised and has their packages poisoned, then developer C who uses B's package, all within a matter of minutes. With this change, if everyone had a minimum delay of one day, then B could not be compromised until a day after A, and C a day after that. This would very effectively limit spread.
Reacted by leah, Hideki Igarashi, Kirill Groshkov, Karl Horky, Limon Monte, Simon, Will Slattum, david, Brandon Hall, cnoble-may and 1 more
The Dependabot cooldown is per version, to be clear, so not the strategy outlined by @limonte, which I think is superior i.t.o. guaranteeing that you hold off until a release + patches have been completed.
some-cool-package has 6.6.6 with a bug released 4 days ago and 6.6.7 with bug-fix released 2 days ago
with --before="3 days" the buggy version would be installed which would be the exact opposite of desired
with --stabilityDays=3 neither 6.6.6 nor 6.6.7 would be installed which is desired because 6.6.6 is buggy and 6.6.7 is immature.
I think from the information from the registry, there is no way to know that 6.6.6 would be buggy and that you would not want this version. It could very well be the case that 6.6.6 is totally fine for your project, and 6.6.7 was an unrelated bump or readme change or something like that. This can even happen for projects that try to follow strict semver.
I think given that reason and the other reasons mentioned above, and the fact that the JS package manager ecosystem has chosen the simple time-from-present-day minimumReleaseAge method:
@RiskyMH exactly what I need and what I described in this RFC, thanks a lot for this! Bun is amazing, I use it for all my projects including 18k ⭐ SweetAlert2
My first experience was negative because of binary lock file (what a terrible idea that was!). But after introducing text lock file Bun is the winner for me. Keep up the good work 💪
With GitHub Copilot I analyzed the Bun code and it confirms the behavior you mentioned:
Activity
ljharb commentedon Oct 18, 2022
From #549:
nlf commentedon Jan 5, 2023
i'm a little confused how
--beforedoesn't solve for this.. yes, you have to form the timestamp yourself but it won't install anything that's newer than the date listed, so if you give it a timestamp for 3 days ago that should be pretty close to what you're asking for. at install time we evaluate for and do our best to avoid known vulnerable versions of packages, however if there's no semver-compatible alternative then we don't have much choice but to go ahead and install it.the second part of that problem will be addressed by audit policies which would be capable of refusing the installation of any package with a known vuln.
castarco commentedon Jun 29, 2023
I introduced a new proposal that could accommodate a specific metadata field for this purpose (the goal of my proposal is entirely different, but certainly not incompatible).
In the #703 RFC I propose to introduce a new
updatePolicysection to provide finer grained control over how we update our dependencies (direct and transitive). We could introduce there a field calledminReleaseMaturitythat accepted temporal interval values in format ISO 8601; then, if the package manager respects that field, it should never update to a dependency that is not "mature enough".In any case, I agree with this remark from @ljharb: #646 (comment)
Jxck commentedon Sep 15, 2025
@ljharb
Retrospective from Nx incidents, It's not always true.
GHSA-cxm3-wv7p-598c
In some cases, Attackers may be able to add malicious commits or publish poisoned packages within a short timeframe.
Even if such attacks are easily detected by maintainers, it has been found that they can affect many developers during the several-hour time lag before maintainers can recover appropriately.
While this feature may not be a fundamental solution for attack, it can be expected to act as a fail-safe mechanism that reduces the potential for many developers to be harmed.
pnpm decided to release this feature, probably in response to the lessons learned from Nx.
pnpm/pnpm#9921
Having npm itself, which has a larger user base, incorporate this feature would be beneficial for the entire JavaScript ecosystem.
Any comments are welcome.
uncenter commentedon Sep 15, 2025
Also, there now exists software such as https://socket.dev/ that can seemingly detect malware in published packages regardless of whether users install it or not. From https://socket.dev/blog/nx-packages-compromised:
So I'd argue there's still a good chance of vulnerability discovery even if the ecosystem moves toward this approach.
(Socket also published https://socket.dev/blog/pnpm-10-16-adds-new-setting-for-delayed-dependency-updates about the new pnpm option, but I don't see any mention how their software would play into it.)
khaosdoctor commentedon Sep 16, 2025
While I agree with this in parts:
I also agree with @uncenter and this tweet because, as of today, 3 years later, the ecosystem has evolved quite a bit and we manage to pick up these vulnerabilities sooner than before and it would be a nice addition to the package manager if we could have this feature. We also use renovate here and this feature not only helps us to avoid zero day vulns but also allows for a more stable and consistent update of the packages.
For example, if we update a package on the day a new update is out it might contain bugs, if we wait 5 days and then update, these bugs will be more discoverable and fixed. Unless the entire Node.js community decides to use the same lead time to wait, we will still benefit from that. So it's a heavy +1 for me to have it here
But I also agree to this, it's not a package manager problem, it's a repository issue. One that could be mitigated if NPM (the repository), for example, added something like:
Are these ultimate solutions? Absolutely not, but they should help mitigate the bulk of supply chain attacks
limonte commentedon Sep 16, 2025
It's nice to see some activity on my RFC after several years of silence 😄
@Jxck
No, pnpm did not release the proposed here feature. They did not implement
stabilityDays, butminimumReleaseAge, these 2 are not the same. No lessons are learned from Nx so far, sorry 😄Here's the quote from my request:
pnpm's feature
minimumReleaseAge=3dayswould install 6.6.6 which is the worst possible option. It's just something similar to npm's long-existing--beforeparamMore details on that here: pnpm/pnpm#9921 (comment)
averms commentedon Sep 16, 2025
Can you explain how exactly
stabilityDayswould be implemented?limonte commentedon Sep 16, 2025
@averms
stabilityDaysis the duration between the release date of a given release and [the next release date OR now if there's no next release]Interestingly enough in Renovate it was called
stabilityDaysbefore and they renamed it tominimumReleaseAge: https://docs.renovatebot.com/configuration-options/#minimumreleaseageBut Renovate's
minimumReleaseAgeis not the same as pnpm'sminimumReleaseAge. Renovate'sminimumReleaseAgeis what I name asstabilityDaysand pnpm'sminimumReleaseAgeis basically an alias for the npm's --before paramzkochan commentedon Sep 16, 2025
I have implemented this feature in pnpm and I see two features that are missing on the registry side.
pnpm add eslint@latest) and the latest version is not mature enough, we do not know what other versions were ever released as "latest". It would be good to store every dist-tag that was used in the past. Alternatively, we could throw an error in this case. For now I just pick the biggest version that has the same major version as the active "latest" and satisfies the time constraint.khaosdoctor commentedon Sep 17, 2025
If I understood correctly, this would require some sort of standardization in release tags won't it? Like, people will need to agree what is a "releasable" version and tag it accordingly, otherwise we won't know which is an actual release. Or we are just assuming every tag should be treated as a dist release?
karlhorky commentedon Sep 17, 2025
@limonte I think this RRFC should be reconsidered
The behavior of evaluating the age between each release would lead to very unusual, unwanted behavior, in the case of fast-releasing packages.
Eg. it would lead to recursive checks of each version back to the original version and then blocked install of ANY version in such a case:
--stabilityDaysof eg. 7 days (or longer, like 14 days)This compounds if
--stabilityDaysshould also be applied to transitive dependencies - which it should, because security vulnerabilities in transitive dependencies are often still relevant. Given--stabilityDays=7with your current algorithm, that means that all transitive dependency versions (versions of dependencies specified by other dependencies) would also need to satisfy this "7 days stable" range, which would further reduce the installability of any packages.It could be argued that users "should know" about which dependencies release fast and for them to configure manual exceptions to
--stabilityDays, but that puts more pressure on end users to know their entire dependency graph and perform complex configuration.Probably what is best (outside of a completely new algorithm not yet discussed, see edit below) is to keep it simple like pnpm's
minimumReleaseAgealgorithm, which counts a negativeminimumReleaseAgefrom the present day instead of between previously released versions. This has also been proposed for npm over here:Edit: Addon to algorithm proposed by @RiskyMH for Bun:
So I read this as: only apply @limonte's "stability check" algorithm to versions within max 7 days backwards. This seems to mitigate the concerns with fast-releasing packages, and avoids the recursive check beyond those 7 days.
bun installoven-sh/bun#22679 (comment)ljharb commentedon Sep 17, 2025
If this option is not defaulted, it will not help the users who need its protection the most.
If it is defaulted (or sufficiently adopted), it will simply delay discovery once attackers adapt, defeating the purpose in the first place.
karlhorky commentedon Sep 18, 2025
@ljharb This may have been historically true, where end users were the unfortunate ones to discover the vulnerabilities. But given the intense attention of security companies and researchers on attacks right now (Socket finding 1000 attacks a week currently) and the possibilities of future automation (eg. automated security check queuing built into npm), maybe that is shifting?
Security companies and researchers would configure away from the default (down to 0 days) and have a period of one or more days to do their work before the wider ecosystem is affected.
The registry having more integrated, automated security checks, like many have suggested on GitHub and Twitter in the last days, would be of course even better, but seems like that is a structural change which will take longer to push through.
3 remaining items
zkochan commentedon Sep 18, 2025
If you check how the node.js index file looks like. It is somewhat similar. Every version of node.js is marked with lts true/false.
khaosdoctor commentedon Sep 18, 2025
True, that could be achieved
bakkot commentedon Sep 19, 2025
This is not true, because it is possible to discover these things without anyone installing them. Besides the mentioned security scans, many attacks take the form of account takeovers where the account holder is likely to notice fairly quickly. Not all malicious packages would identified in time, but many would, and security is not a binary.
However, even if this were true, the feature being on by default would still have the advantage of adding a delay to the speed with which worms can spread in the registry. Right now developer A can be compromised and have their packages poisoned, then developer B who uses A's packages is compromised and has their packages poisoned, then developer C who uses B's package, all within a matter of minutes. With this change, if everyone had a minimum delay of one day, then B could not be compromised until a day after A, and C a day after that. This would very effectively limit spread.
limonte commentedon Sep 20, 2025
BTW for everybody like me who missed the recent Dependabot feature
cooldown: Dependabot supports configuration of a minimum package agestefanv commentedon Sep 20, 2025
The Dependabot cooldown is per version, to be clear, so not the strategy outlined by @limonte, which I think is superior i.t.o. guaranteeing that you hold off until a release + patches have been completed.
maint: Set before prior to the shai hulud supply chain attack (#608)
Lionad-Morotar commentedon Apr 4, 2026
For anyone landing here looking for the implementation status:
min-release-agefeature has been implemented and shipped in npm CLIImplementation
Usage
karlhorky commentedon Apr 4, 2026
@limonte now that npm/cli#8965 has been selected as the path forward and there has been some discussions of the downsides of the alternative proposal of "minimum maturity" (eg. my comment above discussing the blocker with fast-releasing packages), do you think it would be ok to close this RFC issue?
To add one more point, in case my comment above is not enough:
Regarding this quote of your original motivation in the issue description:
I think from the information from the registry, there is no way to know that
6.6.6would be buggy and that you would not want this version. It could very well be the case that6.6.6is totally fine for your project, and6.6.7was an unrelated bump or readme change or something like that. This can even happen for projects that try to follow strict semver.I think given that reason and the other reasons mentioned above, and the fact that the JS package manager ecosystem has chosen the simple time-from-present-day
minimumReleaseAgemethod:min-release-agenpmMinimalAgeGateminimumReleaseAgeminimumReleaseAge--minimum-dependency-ageminimumReleaseAgecooldown--cooldown...this RFC issue can be closed. Do you agree?
limonte commentedon Apr 4, 2026
Thank you for the good summary @karlhorky
IMO yes, this is done 👍
limonte commentedon Apr 4, 2026
It only took 3 years and several critical attacks to do this. Better late than never 😄
Thank you all who participated in discussions and implementations across different package managers ❤️
karlhorky commentedon Apr 4, 2026
@limonte thanks to you too for the proposal and engagement! ❤️
RiskyMH commentedon Apr 4, 2026
@karlhorky I'm not sure about the others but in Bun, I did implement it to do stability with the age (capped at 7 days) :)
limonte commentedon Apr 4, 2026
@RiskyMH exactly what I need and what I described in this RFC, thanks a lot for this! Bun is amazing, I use it for all my projects including 18k ⭐ SweetAlert2
My first experience was negative because of binary lock file (what a terrible idea that was!). But after introducing text lock file Bun is the winner for me. Keep up the good work 💪
With GitHub Copilot I analyzed the Bun code and it confirms the behavior you mentioned: