In roughly six minutes on May 11, an attacker pushed 84 malicious versions across 42 @tanstack/* packages. Six minutes. If your CI happened to run an update during that window, you shipped malware. If it ran the next morning, you were fine. That gap between "live for hours" and "installed days later" is the entire idea behind the dependency cooldown, and in the past six months nearly every package manager you use has quietly added one.
The reflex that became the wound
For most of the last decade the implicit deal with open-source package management was simple: install the latest, install it now. Lockfiles pinned versions once you committed them, but npm install, bundle update, and pip install would all happily resolve to a version published seconds ago. We treated freshness as a virtue. Attackers noticed.
A cooldown inverts that reflex. It is a time-based filter that refuses to resolve a package version until it has been public for at least N days. During that window the community, the scanners, and the registry's own abuse teams get a chance to spot a poisoned release before it ever touches your build. It is deliberately boring, and boring is exactly what a good security control should be.
As of June 3, 2026, Ruby's Bundler 4.0.13 shipped a cooldown: option. It joined a genuinely unusual wave. Within about six months, npm (min-release-age, v11.10.0), pnpm (minimumReleaseAge), Yarn (npmMinimalAgeGate), Bun, and Deno all added native cooldown support, while Python's uv and pip (--uploaded-prior-to, v26.0) added date- and duration-based variants. Cross-ecosystem agreement on a single control almost never happens. It happened here because the bodies kept piling up.
Why everyone moved at once
The trigger was a brutal stretch of attacks. March 2026 alone produced three major compromises in under two weeks: Trivy, LiteLLM, and axios, a package that pulls north of 100 million downloads a week. Then the TanStack incident in May. The pattern across all of them is the same, and it is the whole reason cooldowns work: the attacker needs the bad version live for a few hours, and the defender just needs to not install it for a few days. Those two timelines barely overlap.
The numbers make the case better than any vendor pitch. William Woodruff's analysis of ten prominent supply chain attacks found that eight had an exploitation window of under one week, and all but one were detected and pulled within two weeks. The axios malware was caught roughly three hours after release. The s1ngularity attack was live about four hours. Datadog's security team concluded that a 12-hour cooldown would have blocked both of those incidents entirely, and a seven-day window would have neutralized the large majority of the historical sample.
The economics are absurdly lopsided
I have spent enough time defending build pipelines to be deeply skeptical of any control that promises a lot for free. Most don't. They demand an agent, a scanner subscription, a triage queue, and a security engineer to babysit the false positives. In practice the thing that quietly kills these programs is the per-package toil: somebody has to look at every alert, and eventually nobody does.
Cooldowns are the rare exception. There is no new infrastructure, no daemon, no per-package decision to make. It is opt-in configuration, one line in most cases. The cost is bounded and predictable: you adopt new versions a few days later than you otherwise would. And here is the part teams overestimate. Almost nobody is actually racing to consume a release minutes after it ships. The mental image of "I need the patch the instant it lands" is real for maybe one or two dependencies in your tree, not the other four hundred. For everything else, the delay is invisible. You were going to update on your sprint cadence anyway.
What to actually turn on
This is not theoretical, and you do not need to wait for tooling to catch up. The knobs exist today:
- npm:
min-release-age=7(days). - pnpm:
minimumReleaseAge, measured in minutes. - Yarn:
npmMinimalAgeGate. - Bundler 4.0.13+:
source "https://rubygems.org", cooldown: 7in your Gemfile, or via CLI flag or environment variable. This works because rubygems.org's v2 compact index now serves a per-versioncreated_attimestamp, which Bundler reads to do the math.
If you run Dependabot, this gets even easier. GitHub's Dependabot now honors cooldowns natively across npm, Yarn, pnpm, GitHub Actions, and Python, with control by semver type. That last detail matters more than it sounds: you can set a long cooldown on major versions, where the blast radius and the attacker incentive are highest, and a shorter one on patches. That is the right shape for the threat.
On the number itself, the consensus has settled. Seven days is the recommended default. Twelve hours is the aggressive floor that still catches the fastest-detected attacks, and one to three days blocks the median. My advice: start at seven, and only tighten for the handful of dependencies where you can articulate a real reason. If you can't write down why a package needs a 24-hour window, it doesn't.
How the attacks actually run, and where they're heading
It is worth understanding why speed is the attacker's whole game, because it explains why a dumb time delay is so effective. The TanStack compromise didn't brute-force anything. It abused a pull_request_target workflow that ran fork-controlled code with elevated trust, extracted an OIDC token, and then published through the project's own trusted-publisher binding. From the registry's perspective, the releases looked legitimate. The only thing wrong with them was that they were new and malicious, and "new" is precisely the signal a cooldown acts on.
The more worrying shift is where these payloads are aiming. The Mantine-datatable incident in late May 2026 injected a payload runner into .claude/settings.json, .gemini/settings.json, .cursor/rules/setup.mdc, and .vscode/tasks.json. Read that list again. Those are files designed to auto-execute inside AI coding assistants and editors. The attack is no longer content to run at install time in production; it wants to live in the developer's environment and ride their AI tooling. The blast radius now includes the laptop, the editor, and whatever credentials that editor can reach. If you have a habit of cloning an unfamiliar repo and immediately opening it in an AI-enabled editor, that habit is now an attack vector. Treat .vscode/, .cursor/, .claude/, and friends as executable surfaces, and review changes to them the way you'd review a shell script.
Where cooldowns fail, and what they don't replace
A control you trust blindly is a control that will eventually betray you, so be clear about the holes. Cooldowns do nothing against vulnerabilities in already-published code. A Spring4Shell-class flaw that has been sitting in a popular library for two years sails straight through any age filter, because the version isn't new. Cooldowns also lose to a patient attacker: if the payload sits dormant and only fires three weeks after publication, your seven-day window expired long before anything happened. Speed is the common case, not the only case.
There's a second-order risk people miss too. A cooldown delays every new version, including the legitimate emergency CVE patch you genuinely do need today. If your only response to a critical advisory is "the cooldown won't let me," you have turned a security control into a reason to ship vulnerable code. Build the escape hatch before you need it: a documented, auditable way to override the cooldown for a specific version, used rarely and on purpose.
So treat this as one layer. It sits on top of mandatory 2FA, trusted publishing, lockfile verification, and dependency scanning, not in place of any of them. The cooldown closes the fast-attack window. The others close the slow-vulnerability window. You want both shut.
The honest summary is that this is one of the cheapest meaningful upgrades available to a software team right now. The tools already shipped it. The data says it would have stopped most of the attacks that made the news this spring. Turn it on, default to seven days, keep an override for emergencies, and extend your suspicion to the editor config files that the latest attacks are quietly targeting.
Sources
- https://securitylabs.datadoghq.com/articles/dependency-cooldowns/
- https://blog.rubygems.org/2026/06/03/cooldown-let-new-gems-be-vetted.html
- https://nesbitt.io/2026/03/04/package-managers-need-to-cool-down.html
- https://safedep.io/mass-npm-supply-chain-attack-tanstack-mistral/
- https://www.stepsecurity.io/blog/introducing-the-npm-package-cooldown-check
Comments
Be the first to comment.