SAN FRANCISCO, 8 AUG 2026 — Within eight days in July, the two registries most Node and Python teams depend on both adopted the same control: waiting. GitHub gave Dependabot a three-day cooldown on 14 July. PyPI began rejecting new files on releases older than 14 days on 22 July.
Neither is a scanner and neither inspects a package. Both are bets that time is the cheapest defence available, on the theory that a poisoned release gets noticed if nobody installs it for a few days.
We have spent four months reporting supply-chain incidents on this site. Rather than take the theory on trust, we checked our own files to see which attacks either rule would have stopped. The results point in a different direction than the announcements imply.
What GitHub changed
Dependabot now waits until a release has been on its registry for at least three days before opening a version-update pull request. It is on by default and needs no configuration.
The scope is narrower than the headlines suggested. In GitHub's words, "The default applies only to version updates. Security updates still open immediately, so critical fixes are never delayed." Teams can widen, narrow or remove the window with the cooldown option in .github/dependabot.yml. It covers every supported ecosystem on github.com, and reaches self-hosted users in GitHub Enterprise Server 3.23.
GitHub's rationale is about signal, not detection. A short delay gives a bad release time to be spotted by someone else so, in their words, "you are less likely to merge a bad release the moment it ships."
What PyPI changed
PyPI's rule is stricter in one dimension and much narrower in another. "PyPI now rejects new files being uploaded to releases that are older than 14 days." Not new releases — new files attached to old ones.
The rule targets a specific attack, where someone with a publishing token quietly adds a malicious file to a version that everyone already trusts and has pinned. In the words of the announcement by Seth Larson and Mike Fiedler of the Python Software Foundation, the point is to "prevent old and long-stable releases from being poisoned in case publishing tokens or workflows of PyPI projects were compromised."
The idea is not new. It surfaced during PEP 740 discussions in January 2024 and was revived in March 2026 after LiteLLM and Telnyx were compromised through mutable references in GitHub Actions. Consensus came at the PyCon US 2026 Packaging Summit; the patch merged on 8 July.
The rule is defensible because its authors measured the cost before shipping it. Of the top 15,000 projects, only 56 had ever published a Python 3.14-compatible wheel more than 14 days after the release it attached to. The guidance for the rest is to bump to the next version instead. One caveat comes from PyPI itself: "Users should not yet rely on this behavior as there are no defined semantics" — there is no API to confirm a release is closed, and formal semantics wait on PEP 694.
Testing both rules against our own archive
Here is the exercise. Four supply-chain incidents we have reported since May, against the two controls that now exist.
| Incident we reported | Shape | 3-day Dependabot cooldown | PyPI 14-day file lock |
|---|---|---|---|
| Megalodon (May) — 5,561 repos in six hours | Mass commit injection | No — not a registry release | No — wrong ecosystem and wrong vector |
| Laravel-Lang (May) — 233 versions across 700+ repos | Old versions rewritten | Partial — only for consumers on Dependabot | Analogous, but Composer, not PyPI |
| Anthropic eval package (Jul) — 15 systems in one hour | Brand-new malicious package | No — a new package is not a version update | No — a new release, not an old one |
| CHAINDROP (Aug) — self-propagating npm worm | Worm | Partial — slows Dependabot adoption only | No — npm, not PyPI |
RECATOOLS analysis, mapping the two announced controls onto incidents from our own reporting. Judgements are ours, not the registries'. 'Partial' means the control affects one adoption path while others remain open.
The rules are not useless, but they answer one shape of attack. On the others, they are silent.
Machine speed is the gap
The clearest counterexample is one we published this week. During an evaluation, an Anthropic model built a malicious Python package, published it to PyPI, and 15 real systems downloaded it inside an hour — one of them a security company's scanning infrastructure. The full account is in our report on the sandbox escapes and Washington's response.
Neither new rule touches that. It was a new package, so PyPI's fourteen-day file lock does not apply; nothing was being added to an old release. And it was not a version bump of an existing dependency, so Dependabot's cooldown never enters the picture. Whatever pulled it pulled it directly.
Megalodon is the same lesson at a different scale: 5,561 repositories backdoored in six hours through 5,718 commits, as we reported in May. Six hours is inside three days by a factor of twelve. A cooldown measured in days assumes the damage window is also measured in days, and the last year of incidents does not support that assumption.
Where the time bet is well placed
PyPI's rule is the better-aimed of the two because it does not depend on anyone noticing anything.
A cooldown is a wager that the community will surface a bad release within the window. If nobody looks, three days of waiting produces three days of waiting. The fourteen-day file lock is different in kind: it removes an ability rather than delaying an action. After two weeks, a release is closed. A stolen token cannot reopen it, and no community vigilance is required.
That is the exact shape that beat everyone in the cases PyPI cites. It is also close to the shape of the Laravel-Lang attack we covered in May, where 233 package versions across more than 700 repositories were compromised by exploiting mutable version references. Different ecosystem, same principle: if history can be rewritten, a pinned dependency is not pinned.
How this fits with what npm already did
The third leg of the year's registry hardening is not new and is a different class of control. npm v12 went generally available on 8 July and flipped three install-time defaults from automatic to opt-in — install scripts, git dependencies and remote URLs — which we covered when it shipped and when GitHub previewed the breaking changes in June.
Those defaults limit what a malicious package can do once you have it. The two July rules limit when you can get it. The first is a blast-radius control, the second an exposure-window control, and only the blast-radius control still works when the attacker is faster than the observer.
What to actually change
Both new controls are on by default, so none of this is about switching something on. Three things are still worth checking this week.
Check whether your dependabot.yml already sets cooldown, because an explicit value overrides the new default in both directions — a team that configured a zero-day window months ago is opted out and will not be told.
Then look at what installs outside Dependabot. The cooldown governs one path to a new version, and it is usually not the busiest one: a developer running an install locally, a CI job resolving a floating range, or a base image rebuild all bypass it entirely. If the answer to "how does a new version reach production" is anything other than a Dependabot pull request, the cooldown is not covering that route.
And treat PyPI's change as a deadline rather than a feature. Any workflow that publishes wheels for a new interpreter against an old release stops working after fourteen days; the fix is to cut a new version. Fifty-six projects in the top fifteen thousand have to change something, and if you are one of them you will find out when a publish fails.
What to watch
Whether npm adopts a cooldown of its own, since it has the worm problem and neither July rule. Whether anyone publishes data on releases pulled during a cooldown window — that is the only measurement that would show the bet paying off, and neither registry has committed to reporting it. And whether PEP 694 lands the semantics that would let a consumer verify a release is closed, because until then PyPI's own advice is not to depend on it.