Skip to content

[RRFC] Ability to set minimum maturity (in days) of versions to upgrade #646

Closed

Description

@limonte

Motivation ("The Why")

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:

  1. as latest as possible (the obvious one)
  2. free from vulnerabilities, i.e. not present in npm/Snyk/dependabot security databases
  3. 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.

How

Current Behaviour

None.

Desired Behaviour

npm install --stabilityDays=3

References

Renovate has this feature, it's named stabilityDays: https://docs.renovatebot.com/configuration-options/#stabilitydays

Also stabilityDays is mentioned in this RFC: #549

Activity

  1. ljharb commented on Oct 18, 2022

    @ljharb
    Contributor

    From #549:

    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

  2. nlf commented on Jan 5, 2023

    @nlf
    Contributor

    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.

  3. castarco commented on Jun 29, 2023

    @castarco

    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".

    In any case, I agree with this remark from @ljharb: #646 (comment)

  4. Jxck commented on Sep 15, 2025

    @Jxck

    @ljharb

    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

    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.

  5. uncenter commented on Sep 15, 2025

    @uncenter

    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

    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:

    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.

    (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.)

  6. khaosdoctor commented on Sep 16, 2025

    @khaosdoctor

    While I agree with this in parts:

    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:

    1. 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
    2. 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)
    3. 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

  7. limonte commented on Sep 16, 2025

    @limonte
    Author

    It's nice to see some activity on my RFC after several years of silence 😄

    @Jxck

    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

    More details on that here: pnpm/pnpm#9921 (comment)

  8. averms commented on Sep 16, 2025

    @averms

    Can you explain how exactly stabilityDays would be implemented?

  9. limonte commented on Sep 16, 2025

    @limonte
    Author

    @averms stabilityDays is the duration between the release date of a given release and [the next release date OR now if there's no next release]

    version release date releaseAge stabilityDays
    6.0.0 10 days ago 10 days 6 days
    6.6.6 4 days ago 4 days 2 days
    6.6.7 2 days ago 2 days 2 days

    Interestingly enough in Renovate it was called stabilityDays before and they renamed it to minimumReleaseAge: https://docs.renovatebot.com/configuration-options/#minimumreleaseage

    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

  10. zkochan commented on Sep 16, 2025

    @zkochan

    I have implemented this feature in pnpm and I see two features that are missing on the registry side.

    1. the time field is not included in the abbreviated packument. So, we need to request the full document instead.
    2. 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.
  11. khaosdoctor commented on Sep 17, 2025

    @khaosdoctor

    I have implemented this feature in pnpm and I see two features that are missing on the registry side.

    1. the time field is not included in the abbreviated packument. So, we need to request the full document instead.
    2. 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?

  12. karlhorky commented on Sep 17, 2025

    @karlhorky

    @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:

    1. a reasonably longer --stabilityDays of eg. 7 days (or longer, like 14 days)
    2. 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.

  13. ljharb commented on Sep 17, 2025

    @ljharb
    Contributor

    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.

  14. karlhorky commented on Sep 18, 2025

    @karlhorky

    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.

  15. 3 remaining items

  16. zkochan commented on Sep 18, 2025

    @zkochan

    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.

  17. khaosdoctor commented on Sep 18, 2025

    @khaosdoctor

    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.

    True, that could be achieved

  18. bakkot commented on Sep 19, 2025

    @bakkot

    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.

  19. limonte commented on Sep 20, 2025

    @limonte
    Author

    BTW for everybody like me who missed the recent Dependabot feature cooldown: Dependabot supports configuration of a minimum package age

  20. stefanv commented on Sep 20, 2025

    @stefanv

    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.

  21. Lionad-Morotar commented on Apr 4, 2026

    @Lionad-Morotar

    For anyone landing here looking for the implementation status:

    min-release-age feature has been implemented and shipped in npm CLI

    Implementation

    Usage

    # CLI
    npm install --min-release-age=3
    
    # .npmrc:  x days
    min-release-age=3 
  22. karlhorky commented on Apr 4, 2026

    @karlhorky

    @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:

    • 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:

    ...this RFC issue can be closed. Do you agree?

  23. limonte commented on Apr 4, 2026

    @limonte
    Author

    Thank you for the good summary @karlhorky

    IMO yes, this is done 👍

  24. limonte commented on Apr 4, 2026

    @limonte
    Author

    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 ❤️

  25. karlhorky commented on Apr 4, 2026

    @karlhorky

    @limonte thanks to you too for the proposal and engagement! ❤️

  26. RiskyMH commented on Apr 4, 2026

    @RiskyMH

    @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) :)

    Image

  27. limonte commented on Apr 4, 2026

    @limonte
    Author

    @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:

    Image
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions