How Missing Dependabot Cooldown Configuration Happens in GitHub Actions and How to Fix It
The Vulnerability at a Glance
| Field | Detail |
|---|---|
| Vulnerability | Missing Dependabot Cooldown Period |
| CWE | CWE-1104 — Use of Unmaintained Third-Party Components |
| Language | YAML (GitHub Dependabot configuration) |
| Risk | Automatic proposal of malicious or unstable newly published packages |
| Root Cause | No cooldown block defined for github-actions or cargo ecosystems |
| Fix | Add cooldown: default-days: 7 to each package-ecosystem entry |
Direct Answer
A missing Dependabot cooldown (CWE-1104) in
.github/dependabot.ymlallowed Dependabot to immediately propose updates to newly published packages — including potentially malicious or typosquatted versions. The fix adds acooldownblock withdefault-days: 7to eachpackage-ecosystementry (bothgithub-actionsandcargo) so no update PR is opened until a package version has been publicly available for at least 7 days, giving the security community time to identify and report malicious releases.
Introduction
The .github/dependabot.yml file is the gatekeeper for automated dependency updates. It decides which package ecosystems to watch, how often to check for new versions, and — critically — how cautiously to act when a new version appears. In this project's configuration, both the github-actions and cargo ecosystems were configured to run on a daily schedule, but neither had any restriction on how new a package version could be before Dependabot proposed it as an update.
That gap — the absence of a cooldown block — meant that a package published at midnight could generate a Dependabot pull request by morning. In a world where supply chain attacks against open-source ecosystems are increasingly common, that's a meaningful attack surface.
The Vulnerability Explained
What Is a Dependabot Cooldown?
GitHub's Dependabot version updates feature supports a cooldown configuration block that tells Dependabot to wait a specified number of days after a package version is first published before proposing it as an update. Without it, Dependabot operates on a "latest is best" assumption — which is fine for stability, but dangerous for security.
The Vulnerable Configuration
Here is the relevant section of .github/dependabot.yml before the fix, starting at line 10:
# Maintain dependencies for github-actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
# Maintain dependencies for cargo
- package-ecosystem: cargo
directory: "/"
schedule:
interval: "daily"
target-branch: "main"
Both ecosystems — github-actions and cargo — check for updates daily. Neither has any cooldown block. This means the moment a new version of any watched package is published to the registry, it becomes a candidate for a Dependabot PR.
How Could This Be Exploited?
Consider a concrete attack scenario targeting the cargo ecosystem in this project:
- An attacker identifies a popular crate that this project depends on — say, a utility crate with a small but active maintainer team.
- The attacker compromises the maintainer's crates.io credentials (via phishing, credential stuffing, or a leaked token) and publishes a new patch version, e.g.,
my-util = "1.2.4", containing a backdoor or credential-stealing payload. - Within 24 hours, Dependabot opens a PR proposing the update from
1.2.3to1.2.4. The PR looks routine — a patch bump with a changelog entry. - A developer reviews the PR, sees a small version bump, trusts the automated tooling, and merges it.
- The malicious code is now in the build, potentially exfiltrating secrets, injecting code into CI artifacts, or establishing persistence.
The same scenario applies to the github-actions ecosystem: a compromised action version could exfiltrate GITHUB_TOKEN, repository secrets, or modify build outputs.
The critical window here is the first 24–72 hours after a malicious package is published. Security researchers, automated scanning tools, and the registry's own abuse detection systems typically need days to identify and respond to a malicious release. A cooldown period of 7 days means Dependabot simply won't propose the update during the highest-risk window.
Real-World Context
This isn't theoretical. High-profile supply chain incidents — including the event-stream npm compromise, the ua-parser-js hijacking, and numerous malicious crates published to crates.io — all shared a common pattern: the malicious version was published and consumed by automated tooling within hours of appearing on the registry.
The Fix
What Changed
The fix adds a cooldown block with default-days: 7 to both package-ecosystem entries in .github/dependabot.yml. Here is the complete diff:
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -11,6 +11,8 @@ updates:
directory: "/"
schedule:
interval: "daily"
+ cooldown:
+ default-days: 7
# Maintain dependencies for cargo
- package-ecosystem: cargo
@@ -18,3 +20,5 @@ updates:
schedule:
interval: "daily"
target-branch: "main"
+ cooldown:
+ default-days: 7
Before and After
Before (vulnerable — no cooldown on either ecosystem):
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: cargo
directory: "/"
schedule:
interval: "daily"
target-branch: "main"
After (fixed — 7-day cooldown on both ecosystems):
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
cooldown:
default-days: 7
- package-ecosystem: cargo
directory: "/"
schedule:
interval: "daily"
target-branch: "main"
cooldown:
default-days: 7
Why 7 Days?
Seven days is the GitHub-recommended baseline for default-days. It balances two competing concerns:
- Security: Most malicious packages are identified and reported within 24–72 hours of publication. A 7-day window provides substantial coverage against the peak attack window.
- Velocity: Most legitimate patch and minor version updates are stable within a week. Teams still receive timely security patches; they just aren't racing to apply them on day zero.
For higher-risk environments, default-days: 14 or even default-days: 30 may be appropriate. The cooldown block also supports per-semver-range overrides:
cooldown:
default-days: 7
semver-patch-days: 3 # faster for patch versions
semver-minor-days: 7 # standard for minor versions
semver-major-days: 14 # extra caution for major versions
Why Both Ecosystems Needed the Fix
It might be tempting to think the github-actions ecosystem is lower risk than cargo because actions run in a sandboxed CI environment. That assumption is incorrect: a compromised GitHub Action can read all repository secrets, modify build artifacts, exfiltrate source code, and abuse the GITHUB_TOKEN for repository operations. Both ecosystems represent genuine supply chain attack surfaces and both required the fix.
Prevention & Best Practices
1. Always Define a Cooldown in dependabot.yml
Every package-ecosystem entry should include a cooldown block. Treat it as a required field, not an optional one. Code review checklists and PR templates for changes to .github/dependabot.yml should verify its presence.
2. Use Static Analysis to Enforce Configuration Hygiene
The Semgrep rule package_managers.dependabot.dependabot-missing-cooldown.dependabot-missing-cooldown detects this exact pattern. Add it to your CI pipeline so that any future edits to dependabot.yml that remove the cooldown block are caught before merge.
3. Require PR Reviews for Dependabot Updates
Even with a cooldown, Dependabot PRs should require at least one human review. Enable branch protection rules that prevent auto-merge of dependency update PRs without approval.
4. Combine Cooldown with Dependency Review
GitHub's Dependency Review Action can block PRs that introduce known-vulnerable packages. Used together with a cooldown period, it creates a two-layer defense: time-based filtering plus vulnerability-database filtering.
5. Monitor Registry Advisories
Subscribe to security advisories for your key ecosystems:
- Rust/Cargo: RustSec Advisory Database
- GitHub Actions: GitHub Security Advisories
6. Regression Testing
The PR includes a Rust test that validates the security invariant programmatically:
#[test]
fn test_dependabot_config_maintains_cooldown_security_boundary() {
// Parses dependabot.yml and asserts that every package-ecosystem
// entry has cooldown.default-days >= 7
}
This kind of configuration-as-code testing is valuable: it turns a security property into a CI-enforced contract that can't be accidentally removed.
Key Takeaways
- The
cooldownblock is not optional — omitting it from anypackage-ecosystementry independabot.ymlcreates a supply chain risk window during which malicious package versions can be automatically proposed for merge. - Both
github-actionsandcargoecosystems were affected — neither entry in this project'sdependabot.ymlhad a cooldown, meaning both CI infrastructure and application dependencies were exposed. - 7 days is the recommended minimum — it covers the peak window for malicious package detection without significantly delaying legitimate security patches.
- Daily schedule + no cooldown = maximum exposure — the
interval: "daily"setting on both ecosystems amplified the risk by minimizing the time between publication and PR creation. - Static analysis can and should enforce this — the Semgrep rule that caught this issue can be integrated into CI to prevent regressions, making this a one-time fix rather than a recurring audit item.
How Orbis AppSec Detected This
- Source: The
.github/dependabot.ymlconfiguration file, specifically thepackage-ecosystementries at lines 10 and 18, which define which registries Dependabot monitors for new package versions. - Sink: The absence of a
cooldownblock means Dependabot's update proposal logic has no time-based gate — any newly published version immediately becomes a candidate for a PR. - Missing control: No
cooldown: default-daysvalue was set for either thegithub-actionsorcargoecosystems, removing the only built-in mechanism to filter out newly published (and potentially unvetted) package versions. - CWE: CWE-1104 — Use of Unmaintained Third-Party Components (extended to cover unvetted newly published components).
- Fix: Added
cooldown: default-days: 7to bothpackage-ecosystementries in.github/dependabot.yml, enforcing a 7-day waiting period before any new package version is proposed as an update.
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 two-line addition to a YAML configuration file — cooldown: and default-days: 7 — closes a meaningful gap in this project's supply chain security posture. The missing cooldown wasn't a bug in application code; it was a misconfiguration in the automated tooling that manages dependencies. That distinction matters: misconfigured security tooling can be just as dangerous as vulnerable application code, and it's often overlooked precisely because it lives in configuration files rather than source files.
The fix here is simple, but the principle it embodies is important: automated dependency updates should be cautious, not eager. Dependabot is a powerful tool for keeping dependencies current, but without a cooldown period, it can become an unwitting delivery mechanism for supply chain attacks. Adding a 7-day buffer gives the security ecosystem time to do its job before your team is asked to act.
Review your own dependabot.yml configurations today. If you don't see a cooldown block on every package-ecosystem entry, add one.
References
- CWE-1104: Use of Unmaintained Third-Party Components
- OWASP A06:2021 – Vulnerable and Outdated Components
- GitHub Docs: Dependabot
cooldownconfiguration option - GitHub Docs: Dependabot version updates configuration reference
- Semgrep rule: dependabot-missing-cooldown
- RustSec Advisory Database
- fix: this dependabot configuration does not set a co... in...