The Race to Cool Down Dependencies: How Package Managers Are Fighting Supply Chain Attacks
#Security

The Race to Cool Down Dependencies: How Package Managers Are Fighting Supply Chain Attacks

Tech Essays Reporter
7 min read

A comprehensive analysis of the rapid adoption of dependency cooldowns across package managers as a defense against supply chain attacks, examining implementation patterns, ecosystem differences, and the ongoing challenges in securing software supply chains.

The software supply chain has become a battleground, with attackers increasingly targeting package registries to inject malicious code into thousands of projects through compromised maintainer accounts. In response, the package manager ecosystem has undergone a remarkable transformation over the past year, with tools across multiple languages rapidly adopting dependency cooldown features that delay the installation of newly published packages.

The Problem That Sparked a Movement

The catalyst for this coordinated response was a simple but powerful observation: when attackers compromise a maintainer's credentials or take over dormant packages, they can publish malicious versions and wait for automated tooling to pull them into projects before anyone notices. William Woodruff's analysis of ten supply chain attacks revealed that eight had windows of opportunity under a week, demonstrating that even modest cooldowns could have blocked most attacks from reaching end users.

Seth Larson crystallized the solution: tools should support a globally-configurable exclude-newer-than=<relative duration> parameter, creating a mandatory waiting period that brings response times for autonomous exploitation back into the realm of human intervention. This would give security researchers and the community time to flag problems before automated builds pull in potentially compromised packages.

The JavaScript Ecosystem's Head Start

JavaScript developers have been at the forefront of this movement, with five major package managers implementing cooldown features within six months—an unprecedented pace of coordinated feature adoption across competing tools.

pnpm led the charge in September 2025 with minimumReleaseAge in version 10.16, covering both direct and transitive dependencies while providing a minimumReleaseAgeExclude list for trusted packages. Yarn followed the same month with npmMinimalAgeGate in version 4.10.0, also accepting durations in minutes and offering npmPreapprovedPackages for exemptions. Bun added minimumReleaseAge in version 1.3 via bunfig.toml in October 2025, and npm finally shipped min-release-age in version 11.10.0 in February 2026. Deno implemented --minimum-dependency-age for deno update and deno outdated.

The rapid adoption across these competing tools suggests a shared recognition that supply chain security has become a critical concern that transcends individual project preferences.

Python's Measured Approach

Python's ecosystem has taken a more measured path. uv has had --exclude-newer for absolute timestamps since early on and added relative duration support (e.g., "1 week", "30 days") in version 0.9.17 in December 2025, along with per-package overrides via exclude-newer-package. pip shipped --uploaded-prior-to in version 26.0 in January 2026, though it only accepts absolute timestamps, with an open issue about adding relative duration support.

The distinction between absolute and relative timestamps reveals two different goals that happen to share implementation surface: absolute timestamps provide reproducibility by pinning dependency resolution to a specific moment in time, while relative durations create a sliding security window that moves forward with each build.

Community Innovation in Ruby

Ruby's ecosystem presents an interesting case where the community has stepped in where official tools haven't. gem.coop, a community-run gem server, launched a cooldowns beta that enforces a 48-hour delay on newly published gems served from a separate endpoint. This approach is particularly clever because it pushes the cooldown to the index level rather than the client, meaning any Bundler user pointed at the gem.coop endpoint gets cooldowns without changing their tooling or workflow at all.

The Ecosystems Still Catching Up

Several major ecosystems are still in the discussion phase. Cargo has an open issue, with cargo-cooldown providing a third-party wrapper that enforces configurable cooldowns on developer machines as a proof-of-concept. Go has an open proposal for go get and go mod tidy, Composer has two open issues, and NuGet has an open issue though .NET projects using Dependabot already get cooldowns on the update bot side.

The fact that these ecosystems are still discussing the feature while others have already implemented it highlights the varying priorities and development cycles across different language communities.

Dependency Update Tools Lead the Way

Interestingly, dependency update tools were ahead of the curve. Renovate has had minimumReleaseAge (originally called stabilityDays) for years, adding a "pending" status check to update branches until the configured time has passed. Mend Renovate 42 went further by making a 3-day minimum release age the default for npm packages in their "best practices" config via the security:minimumReleaseAgeNpm preset, making cooldowns opt-out rather than opt-in.

Dependabot shipped cooldowns in July 2025 with a cooldown block in dependabot.yml supporting default-days and per-semver-level overrides (semver-major-days, semver-minor-days, semver-patch-days), with security updates bypassing the cooldown. Snyk takes the most aggressive stance with a built-in non-configurable 21-day cooldown on automatic upgrade PRs. npm-check-updates added a --cooldown parameter accepting duration suffixes like 7d or 12h.

The Configuration Chaos

One of the most striking aspects of this rapid adoption is the complete lack of standardization. The feature goes by at least ten different configuration names across tools that support it: cooldown, minimumReleaseAge, min-release-age, npmMinimalAgeGate, exclude-newer, stabilityDays, uploaded-prior-to, min-age, cooldown-days, and minimum-dependency-age. This fragmentation makes writing about the feature almost as hard as configuring it across a polyglot project.

Language vs. System Package Managers

The fundamental difference between language package managers and system package managers explains why cooldowns are necessary in the first place. On npm, PyPI, and RubyGems, running npm publish or gem push makes a package installable worldwide in seconds. If Dependabot or Renovate happens to run in that window, the malicious code lands in projects without human intervention.

System package managers work differently because they separate publishing from distribution. When someone pushes a new version of an upstream library, it doesn't appear in apt install or brew install until a distribution maintainer has reviewed the change, updated the package definition, and pushed it through a build pipeline. Debian maintainers inspect upstream diffs, Fedora packages go through review and koji builds, Homebrew requires a pull request that passes CI and gets merged by a maintainer.

A compromised upstream tarball still has to survive that review process before reaching anyone's machine, and the people doing the reviews tend to notice when a patch adds an obfuscated postinstall script that curls a remote payload. Cooldowns on the language package manager side are essentially trying to retrofit something like that review window onto ecosystems that never had one.

The Timezone Problem

Even the seemingly simple question of what "seven days ago" means gets complicated when your CI server is in UTC, your developer laptop is in US Pacific time, and the registry timestamp uses whatever timezone PyPI's servers happen to be configured with. A few hours of timezone drift can determine whether a package published six days and twenty-two hours ago passes the cooldown check or not.

The discussion around pip's duration parsing reveals how these two modes serve different goals that happen to share implementation surface. ISO 8601 durations (P7D) are unambiguous but nobody wants to type them, human-readable strings like "7 days" are friendly but need a parser that maintainers would rather not write and maintain, and variable-length calendar units like months and years require knowing which month you're in to convert to a concrete number of days.

The Road Ahead

The rapid adoption of dependency cooldowns represents a significant shift in how the software community approaches supply chain security. What started as a niche concern has become a mainstream feature across multiple ecosystems, with tools racing to implement protections that give the community time to respond to potential threats.

However, challenges remain. Ecosystems like Cargo, Go, Bundler, Composer, and pip are still catching up, meaning developers in those communities must rely on dependency update tools to enforce delays. Even then, nothing stops someone from running cargo update or bundle update locally and pulling in a version that's been on the registry for ten minutes.

For ecosystems like Maven, Gradle, Swift Package Manager, Dart's pub, or Elixir's Hex, there's no cooldown discussion at all, leaving them potentially vulnerable to the same supply chain attacks that have plagued other ecosystems.

The fragmentation in configuration names and implementation details also creates friction for polyglot projects that need to coordinate cooldown policies across multiple languages and tools.

Despite these challenges, the past year has demonstrated that when faced with a clear and present danger to software supply chains, the package manager ecosystem can move quickly and decisively. The question now is whether this momentum will continue until all major ecosystems have robust cooldown protections, or whether the rapid pace of adoption will give way to the usual fragmentation and inconsistency that characterizes much of the software development landscape.

The answer will determine whether dependency cooldowns become a standard security practice or remain a patchwork of inconsistent implementations that leave gaps for attackers to exploit.

Comments

Loading comments...