Skip to content
Fran Gonzalez
← Back to blog
(updated Jun 5, 2026) · Clanker · 5 min read

Automated Dependency Updates with Supply Chain Security

How to set up Renovate or Dependabot with cooldown policies that complement pnpm's minimumReleaseAge — automated patches without opening the door to compromised packages.

The previous post covered static supply chain defenses — tool pinning, pnpm workspace settings, CI hardening. But one gap remained: dependency updates. I was updating packages manually, which meant either falling behind on security patches or adopting new releases the moment they shipped. Neither worked.

Automated dependency bots solve the velocity problem. Without cooldown policies, they can become the delivery mechanism for the next supply chain attack.

The Problem

In September 2025, the Shai-Hulud attack compromised over 500 npm packages in a coordinated campaign. Malicious versions were published, and projects with automated updates that had no cooldown window adopted them within minutes. The attack was discovered and the malicious versions removed within hours — but for projects using auto-merge, the damage was already done.

Update automation needs a cooling-off period. Most compromised packages are discovered and removed from the registry within 24 hours. A short delay between publication and adoption catches the majority of attacks without meaningfully slowing down legitimate updates.

How Cooldowns Work

There are three layers of cooldown in a typical JavaScript project:

LayerToolCoverageWhat it does
Package managerpnpm minimumReleaseAgeFull tree (transitive too)Blocks install of packages published less than N minutes ago
Update botRenovate minimumReleaseAge or Dependabot cooldownDirect dependencies onlyDelays PR creation until a package version is N days old
CI gateStepSecurity npm Package CooldownPR-levelBlocks merge if a PR introduces a version published within N days

The package manager setting catches transitive dependencies that the update bot doesn’t manage. The CI gate catches manual pnpm add commands that bypass the bot entirely.

Renovate Configuration

Renovate’s cooldown is configured with minimumReleaseAge:

{
  $schema: "https://docs.renovatebot.com/renovate-schema.json",
  extends: ["config:recommended"],
  minimumReleaseAge: "3 days",
  minimumReleaseAgeBehaviour: "timestamp-required",
}

Renovate will not create a PR for any package version published less than 3 days ago. If the latest release is too fresh, it picks the most recent version that satisfies the age requirement.

For security-specific updates, I can create a package rule that bypasses the cooldown:

{
  packageRules: [
    {
      matchUpdateTypes: ["pin", "digest"],
      minimumReleaseAge: "0 days",
      description: "Allow immediate pin and digest updates",
    },
  ],
}

Renovate’s minimumReleaseAge only applies to direct dependencies. Transitive dependencies — the ones pulled in by your dependencies — are not covered. That’s where pnpm’s minimumReleaseAge fills the gap, since it applies to the full dependency tree during resolution.

Dependabot Configuration

Dependabot’s equivalent is the cooldown option:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "daily"
    cooldown:
      default-days: 3
      semver-major-days: 30
      semver-minor-days: 7
      semver-patch-days: 3

The semver-specific overrides are worth setting — major version bumps warrant a longer cooling-off period since they’re more likely to introduce breaking changes or unexpected behavior.

Dependabot’s cooldown also only applies to direct dependencies. Like Renovate, it complements pnpm’s minimumReleaseAge rather than replacing it.

The Gap: Transitive Dependencies

Neither Renovate nor Dependabot manages transitive dependencies. If a package you depend on adds a new transitive dependency that was published minutes ago, neither bot will catch it. pnpm’s minimumReleaseAge does — it applies during resolution, regardless of whether the dependency is direct or transitive.

The two layers need each other:

  • Renovate/Dependabot → creates PRs, manages direct dependency updates, respects its own cooldown
  • pnpm minimumReleaseAge → catches transitive dependencies, applies at install time, covers the full tree

Putting It Together

A minimal setup that covers all three layers:

  1. pnpm-workspace.yamlminimumReleaseAge: 1440 (covers full tree)
  2. Renovate or Dependabot — cooldown of 3+ days (manages direct deps)
  3. CI steppnpm audit --prod --audit-level high (catches known vulnerabilities)

The cooldown values should be long enough for the community to discover attacks (24+ hours) but short enough that security patches don’t sit in limbo for weeks. I started with 3 days and adjusted from there.

What I’d Do Differently

I’d set up Renovate earlier. The manual update workflow meant I was either behind on patches or anxiously watching for reports of compromised packages after every pnpm update. The cooldown setting removes that tension — updates happen at a pace where the community has time to catch problems.

The one tradeoff: security patches for transitive dependencies still depend on pnpm’s minimumReleaseAge alone. There’s no bot creating PRs for those. A pnpm audit step in CI is the safety net.

What’s Next

Automated updates handle the “get patches faster” problem. But the config that supports them — exclusion lists, allowlists, trust policies — can become stale as the dependency tree evolves. The next post covers keeping supply chain exclusions honest.

References