How Dependabot Missing Cooldown Happens in GitHub Actions and How to Fix It
Introduction
The .github/dependabot.yml file is the quiet workhorse of modern dependency hygiene — it tells GitHub's Dependabot when and how to propose version bumps across your project. But a missing configuration option in this file can silently expose your project — and every downstream consumer — to one of the most insidious supply-chain attack vectors: a malicious package published seconds ago being automatically proposed as an upgrade.
In this repository, a high-severity misconfiguration was detected at line 3 of .github/dependabot.yml: neither the npm ecosystem entry nor the github-actions ecosystem entry defined a cooldown period. Without this setting, Dependabot operates with zero delay — the moment a new version lands on the npm registry or the GitHub Marketplace, it becomes an eligible update candidate. For a Node.js library that downstream applications depend on, the blast radius of merging a compromised dependency is amplified well beyond the repository itself.
The Vulnerability Explained
What "no cooldown" actually means
When Dependabot scans for updates, it compares your pinned versions against the latest available in the configured package ecosystem. Without a cooldown block, the comparison is purely version-based: if a newer version exists, Dependabot opens a pull request. There is no built-in waiting period.
Here is the vulnerable configuration as it existed before the fix:
# .github/dependabot.yml (BEFORE — vulnerable)
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: cron
cronjob: "0 5 10 */2 *"
open-pull-requests-limit: 20
groups:
eslint:
# ...
- package-ecosystem: github-actions
directory: "/"
schedule:
# Check for updates to GitHub Actions every weekday
interval: daily
Neither updates entry contains a cooldown block. This means:
- A package published at 04:59 UTC could appear in a Dependabot PR at 05:00 UTC — one minute after publication.
- There is no opportunity for the npm security team, Snyk, Socket.dev, or community researchers to flag a malicious release before it lands in your review queue.
- Automated CI pipelines that auto-merge Dependabot PRs (a common pattern for patch updates) could merge a compromised package with zero human review.
The specific attack scenario
Consider the following realistic attack chain targeting this Node.js library:
- An attacker identifies a popular transitive dependency of this library — for example, a utility package with a small maintainer team.
- The attacker compromises the maintainer's npm token (via phishing, credential stuffing, or a leaked
.npmrc) and publishes a malicious patch release, e.g.,some-util@2.4.1, which contains a postinstall script that exfiltrates environment variables. - Within hours, Dependabot opens a PR bumping
some-utilfrom2.4.0to2.4.1. - A developer, seeing only a patch version bump and a green CI pipeline (the malicious code runs at install time, not test time), merges the PR.
- Every downstream application that installs this library now executes the malicious postinstall script.
Without a cooldown, step 3 happens before the security community has had any realistic chance to detect and report the malicious release. The Socket.dev research team has documented dozens of real-world attacks that follow exactly this pattern, with the average time-to-detection for malicious npm packages measured in days, not hours.
The Fix
The fix is surgical and precise: add a cooldown block with default-days: 7 to each of the two package-ecosystem entries.
# .github/dependabot.yml (AFTER — fixed)
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: cron
cronjob: "0 5 10 */2 *"
cooldown:
default-days: 7
open-pull-requests-limit: 20
groups:
eslint:
# ...
- package-ecosystem: github-actions
directory: "/"
schedule:
# Check for updates to GitHub Actions every weekday
interval: daily
cooldown:
default-days: 7
Before vs. After
| Aspect | Before | After |
|---|---|---|
npm cooldown |
None — immediate proposals | 7-day waiting period |
github-actions cooldown |
None — immediate proposals | 7-day waiting period |
| Malicious package window | Minutes after publication | At least 7 days |
| Community vetting time | None | Full week |
Why 7 days?
Seven days is the GitHub-recommended default and aligns with real-world incident response timelines. Analysis of historical npm supply-chain incidents shows that the majority of malicious packages are identified and removed within 48–72 hours of publication — but not all. A 7-day window provides a comfortable buffer while still keeping dependencies reasonably current.
The cooldown block also supports semver-patch-days and semver-minor-days for more granular control if you want patch updates to move faster than major version bumps:
cooldown:
default-days: 7
semver-patch-days: 3 # Patch releases wait only 3 days
semver-minor-days: 5 # Minor releases wait 5 days
Prevention & Best Practices
1. Always define a cooldown for every ecosystem
If your dependabot.yml manages multiple ecosystems (e.g., both npm and github-actions as in this case), every single updates entry needs its own cooldown block. A cooldown on one entry does not cascade to others.
2. Combine cooldown with dependency grouping
This repository already uses Dependabot's groups feature to batch related updates (e.g., all ESLint packages together). Combining grouping with a cooldown is a strong pattern: grouped updates reduce PR noise, and the cooldown ensures those grouped updates are only proposed after a vetting window.
3. Audit your auto-merge rules
If your repository has a GitHub Action that automatically merges Dependabot PRs (a common pattern using gh pr merge --auto), ensure your merge criteria include:
- Required status checks passing
- A minimum age on the PR (use branch protection rules or a custom check)
- Dependabot's own security alerts being clear
4. Use additional supply-chain tooling
A cooldown is one layer. Complement it with:
- Socket.dev GitHub App — scans PRs for malicious package behavior
- npm audit in CI — catches known vulnerabilities at install time
- Sigstore/provenance attestations — verify that packages were built from their claimed source
5. Static analysis for configuration files
The Semgrep rule package_managers.dependabot.dependabot-missing-cooldown.dependabot-missing-cooldown will catch this pattern in any repository. Adding Semgrep to your CI pipeline ensures this misconfiguration cannot silently reappear if the dependabot.yml is edited in the future.
Relevant standards
- CWE-1104: Use of Unmaintained Third-Party Components — the broader category covering risks from unvetted dependency updates
- OWASP A06:2021 – Vulnerable and Outdated Components — the OWASP Top 10 category that this misconfiguration directly impacts
- SLSA (Supply-chain Levels for Software Artifacts) — a framework for hardening the full software supply chain, of which dependency update hygiene is a key component
Key Takeaways
- Both ecosystem entries needed the fix independently. The
npmandgithub-actionsentries in thisdependabot.ymleach required their owncooldownblock — there is no global default that covers all ecosystems at once. - A cron-scheduled Dependabot run without a cooldown is especially risky. This repository uses
interval: cronwithcronjob: "0 5 10 */2 *", meaning updates are checked on a fixed schedule. Without a cooldown, a package published the night before could be proposed at exactly 05:00 on the next scheduled run. - Node.js libraries amplify supply-chain risk. Because this is a library (not an application), any compromised dependency it adopts is transitively inherited by all downstream consumers — multiplying the potential impact.
- The
open-pull-requests-limit: 20setting makes cooldown more important, not less. A high PR limit means Dependabot can open many update PRs in a single run, increasing the surface area for a malicious package to slip through during a busy review period. - Seven days is a minimum, not a maximum. For production-critical libraries, consider
default-days: 14or requiring manual approval for major version bumps regardless of cooldown.
How Orbis AppSec Detected This
- Source: The
.github/dependabot.ymlconfiguration file at line 3, where theupdatesarray is defined without anycooldownconstraint on either ecosystem entry. - Sink: The Dependabot update proposal mechanism itself — specifically, the absence of a
cooldownblock means any newly published package version immediately becomes eligible for an automated PR, with no waiting period before it reaches developer review queues. - Missing control: No
cooldown: default-daysvalue was set for either thenpmor thegithub-actionspackage ecosystem entries, removing the only time-based gate that prevents Dependabot from proposing unvetted package versions. - CWE: CWE-1104 — Use of Unmaintained Third-Party Components (by extension, use of unvetted newly published components).
- Fix: Added
cooldown: default-days: 7to both thenpmentry (after thecronjobschedule line) and thegithub-actionsentry (after theinterval: dailyschedule line) in.github/dependabot.yml.
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
A missing cooldown in dependabot.yml is easy to overlook — it is an absence of configuration rather than a piece of broken code, which makes it invisible to most code reviewers. Yet the consequences are concrete: without a waiting period, automated dependency updates become a reliable delivery mechanism for supply-chain attacks, particularly against high-value targets like widely consumed Node.js libraries.
The fix in this case was two four-line additions to .github/dependabot.yml — a trivially small change that closes a meaningful attack window. The broader lesson is that security configuration files deserve the same scrutiny as application code, and that static analysis tools like Semgrep can catch these misconfigurations before they become incidents.
Review your own dependabot.yml files today. If you see an updates entry without a cooldown block, add one.