Back to glossary

Automated Dependency Updates (Dependabot, Renovate)

Automated dependency updates are bots like Dependabot and Renovate that open pull requests bumping your dependencies to newer versions on a schedule.

Also known as Renovate Bot, dependency update bot, Dependabot version updates, Dependabot security updates, automated dependency management

Automated dependency updates are bots, chiefly Dependabot and Renovate (also called dependency update bots), that poll package registries, rewrite your manifest and lockfile to newer versions, and open pull requests on a schedule. They cut patch latency, but they also carry a freshly published release into your CI within hours, the window supply chain attackers exploit.

On March 31, 2026, Renovate opened a pull request on a public repository to adopt axios@1.14.1, the release at the center of the axios compromise, 53 minutes after it was published. It auto-merged the change three minutes later, almost two hours before npm removed the release. The bot did exactly what it was configured to do. An update bot is built to notice a new version fast and make adopting it frictionless, which is exactly what an attacker holding a hijacked maintainer account needs. We think teams should keep their update bots, because a dependency tree that never moves is its own risk. But the same automation should never both choose a version and approve it. The bot should wait out a cooldown, its pull request should run as untrusted code, and nothing should merge until something has inspected what is actually inside the new version.

What is Dependabot?

#

Dependabot is GitHub's built-in update bot: a service that reads your dependency manifests, checks registries for newer versions, and opens pull requests that bump them. It does three related jobs. Dependabot alerts flag dependencies that match a known advisory. Dependabot security updates open a pull request that moves a vulnerable dependency to a fixed version. Dependabot version updates open pull requests for any newer release, on the schedule you set in .github/dependabot.yml.

Renovate is the main alternative. It does the same job on GitHub, GitLab, Bitbucket, Azure DevOps, Gitea, Forgejo and other hosts, and exposes far more policy configuration.

Both tools exist because of how dependency resolution works. A manifest usually declares a range, such as "axios": "^1.13.5" under semantic versioning, and a lockfile records the exact version that range resolved to on the day you installed. Lockfiles make builds reproducible, which also means they never move on their own. Without a bot, the lockfile ages until someone runs an update by hand, and patch latency is measured in months. An update bot turns "a newer version exists" into a reviewable diff. A minimal Dependabot configuration for an npm project looks like this:

YAML
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "daily"
    cooldown:
      default-days: 7
      semver-major-days: 30

What an update bot actually runs

#

Every bot run follows the same six steps, and the order decides where third-party code first executes.

Steps 1 to 5 happen before any human reviews the change

Bot container

dependency scripts ignored
1

Read the manifest and lockfile.

2

Fetch full registry metadata, including the time map. Pick a target that passes ignore rules and the cooldown.

3

Regenerate the lockfile: npm install --ignore-scripts --package-lock-only. New or out-of-range transitive dependencies resolve fresh here.

4

Push dependabot/npm_and_yarn/<pkg>-<version> and open the pull request.

Your CI, on the bot's branch

first execution of the new code
5

npm ci installs the new version and runs its install scripts on npm 11 and earlier. The test suite then imports it, which runs import-time code under any script setting.

Secrets in this job: Dependabot branches get a read-only token and Dependabot secrets only. Renovate branches live in your repository, so they get your Actions secrets.

Merge

human or auto-merge rule
6

The new version reaches the default branch, then every developer machine and deploy.

Figure 1. One update bot run for an npm project, based on dependabot-core's npm updater as of October 2026.

Figure 1, in text: in the bot's own container, the bot (1) reads the manifest and lockfile, (2) fetches each package's full registry metadata, including the time map of publish timestamps, and picks a target version that passes your ignore rules and cooldown, (3) regenerates the lockfile by invoking the package manager, and (4) pushes a branch such as dependabot/npm_and_yarn/<package>-<version> and opens a pull request. Then (5) your CI runs against that branch, installing the new version and importing it in tests. Only then (6) does a human or an auto-merge rule merge it. Steps 1 through 5 all happen before anyone reviews the change.

Step 3 is a real package manager invocation. As of October 2026, Dependabot's npm lockfile updater builds this command:

Bash
npm install <package>@<version> --force --ignore-scripts --package-lock-only \
  [--allow-* flags on npm 12] [--min-release-age=<days>]

--package-lock-only writes the lockfile without populating node_modules, and --ignore-scripts keeps dependency lifecycle scripts from running. Renovate behaves the same way: its ignoreScripts option defaults to true for npm. The bot's container is not where a malicious package runs. Your CI is.

The pull request is the exposure, not the merge

#

The first place a new dependency version executes is usually your CI, on the bot's branch, before any human has read the diff. CI runs npm ci against the regenerated lockfile, which installs the new version and, on npm 11 and earlier, runs its install scripts. The test suite then imports the dependency, which runs any import-time payload regardless of script settings. The lightning release in the Mini Shai-Hulud campaign, for example, ran its credential stealer on import, not at install.

What that CI job can reach depends on which bot opened the branch. GitHub runs workflows that Dependabot triggers with a read-only GITHUB_TOKEN and only Dependabot secrets, never your repository's Actions secrets. Renovate is an ordinary app that pushes branches into your repository, and GitHub passes Actions secrets to pull_request workflows from same-repository branches. By default, a Renovate pull request's CI runs with the same secrets as a teammate's pull request.

Package managers have narrowed the install-time half of this. npm v12, released in July 2026, skips dependency install scripts unless you allow them, and pnpm has done so since version 10. Neither changes import time. We treat every bot branch as untrusted input: no deploy credentials, no publish tokens, and no step that publishes or deploys from it.

How fast can an update bot pull in a compromised release?

#

An update bot can pull in a compromised release within an hour of publication, and the axios compromise is the clearest record of it. The npm registry keeps a publish timestamp for every version in the package document's time map, even after the version itself is removed:

Bash
$ curl -s https://registry.npmjs.org/axios | jq '.time | {"1.14.0", "1.14.1", "1.15.0"}'
{
  "1.14.0": "2026-03-27T19:01:40.915Z",
  "1.14.1": "2026-03-31T00:21:58.168Z",
  "1.15.0": "2026-04-08T16:09:38.623Z"
}
$ curl -s -o /dev/null -w '%{http_code}\n' https://registry.npmjs.org/axios/1.14.1
404

The registry's time map is what every cooldown reads. It exists only in the full package document. The abbreviated document that package managers fetch by default (Accept: application/vnd.npm.install-v1+json) carries just name, dist-tags, versions and modified, which is why enabling a cooldown costs a full metadata fetch.

First 72 hours after axios@1.14.1 was published, to scale

0h24h48h72h
Live on npm: 2 h 53 mRenovate auto-merge at 56 m1-day and 3-day cooldowns end
  1. Mar 30 23:59:12 UTCplain-crypto-js@4.2.1 published −22 m
  2. Mar 31 00:05:41Socket flags plain-crypto-js@4.2.1 as malware −16 m
  3. 00:21:58axios@1.14.1 published, adding plain-crypto-js 0
  4. 01:00:57axios@0.30.4 published +39 m
  5. 01:14:47Renovate opens "Update dependency axios to v1.14.1" +53 m
  6. 01:17:46Renovate auto-merges it +56 m
  7. 03:15npm removes both axios versions +2 h 53 m
  8. Apr 1 00:21:58A 1-day cooldown would first allow 1.14.1, which no longer exists +24 h
  9. Apr 3 00:21:58Dependabot's default 3-day cooldown would first allow it +72 h
Figure 2. Publish times from the npm registry's time field; removal time from the axios postmortem; the Renovate pull request is on a public Codeberg repository.

Figure 2, in text, with times in UTC:

Time (UTC)EventRelative to axios@1.14.1
Mar 30 23:59:12plain-crypto-js@4.2.1, the malicious dependency, published−22 min
Mar 31 00:05:41Socket flags plain-crypto-js@4.2.1 as malware−16 min
00:21:58axios@1.14.1 published with plain-crypto-js added0
01:00:57axios@0.30.4 published+39 min
01:14:47Renovate opens "Update dependency axios to v1.14.1"; auto-merge scheduled one second later+53 min
01:17:46Renovate auto-merges the pull request+56 min
03:15npm removes both axios versions+2 h 53 min
Apr 1 00:21:58A 1-day cooldown would first allow 1.14.1, which no longer exists+24 h
Apr 3 00:21:58Dependabot's default 3-day cooldown would first allow it+72 h

Publish times come from the registry; Socket's detection time comes from our axios compromise report; the removal time comes from the axios postmortem. In that postmortem, lead maintainer Jason Saayman wrote: "The malicious versions were live for about 3 hours before being removed." The repository owner reverted to 1.14.0 at 09:30 UTC, more than six hours after npm had already pulled the release.

The pattern repeats across ecosystems. When attackers compromised arrayref, internment and append-only-vec in the Rust crates compromise, the malicious releases stayed on crates.io for 86, 90 and 107 minutes, according to the Rust Security Response Team. In the eslint-config-prettier compromise in July 2025, we warned that Dependabot and Renovate would pick up the poisoned versions automatically. An update bot that runs several times a day can beat the registry's takedown.

How does a Dependabot cooldown work?

#

A Dependabot cooldown makes Dependabot skip any version younger than a set number of days when it picks an update target. You set it under cooldown with default-days, optional semver-major-days, semver-minor-days and semver-patch-days, and include or exclude lists of up to 150 names. Since July 14, 2026, Dependabot version updates wait 3 days by default even with no cooldown key. Renovate's equivalent, minimumReleaseAge, defaults to off. Its config:best-practices preset turns on a 3-day wait for npm, and Renovate tracks the wait as a renovate/stability-days status check, by default not creating the branch until it passes. The idea is spreading into the dependency cooldown settings of package managers themselves: pnpm 11 waits 24 hours by default, and npm added an opt-in min-release-age setting in version 11.10.

You can reproduce what a cooldown would have done during the axios window. This script resolves a range against the registry's time map as of any timestamp:

JavaScript
// node resolve-at.mjs <package> <range> <ISO time> [cooldown days]
import semver from "semver"; // npm i semver@7
const [name, range, at, days = "0"] = process.argv.slice(2);
const doc = await (await fetch(`https://registry.npmjs.org/${name}`)).json();
const cutoff = new Date(at).getTime() - Number(days) * 86400e3;
const published = Object.entries(doc.time)
  .filter(([v, t]) => semver.valid(v) && !semver.prerelease(v) && Date.parse(t) <= cutoff)
  .map(([v]) => v);
console.log(`${name}@"${range}" at ${at}, cooldown ${days}d -> ${semver.maxSatisfying(published, range)}`);
Bash
$ node resolve-at.mjs axios '^1.13.5' 2026-03-31T01:00:00Z 0
axios@"^1.13.5" at 2026-03-31T01:00:00Z, cooldown 0d -> 1.14.1
$ node resolve-at.mjs axios '^1.13.5' 2026-03-31T01:00:00Z 3
axios@"^1.13.5" at 2026-03-31T01:00:00Z, cooldown 3d -> 1.14.0

Cooldowns gate the version you pick, not the tree you get

#

A bot's cooldown applies to the version the bot selects, but the lockfile regeneration in step 3 runs on a second clock. When the package manager rewrites the lockfile, any transitive dependency that the update adds, or whose locked version no longer satisfies the new range, resolves fresh against the registry at that moment. In the axios attack, the payload lived in exactly such a transitive dependency: plain-crypto-js@4.2.1 was 22 minutes old when axios@1.14.1 shipped.

Until August 2026, Dependabot's cooldown never reached that second clock. A Dependabot maintainer confirmed in the issue tracker: "Cooldown currently only applies to the direct dependency being updated, not transitives." Pull request #15485, merged August 17, 2026, now passes the cooldown into each package manager's native gate: --min-release-age for npm 11.10 and later, minimumReleaseAge for pnpm, and YARN_NPM_MINIMAL_AGE_GATE for Yarn 4.10 and later. The source spells out the limits. The gate is one global value, so Dependabot uses the smallest window among the updated packages. It is skipped entirely when any package in the update is excluded from the cooldown, and it is skipped on npm versions older than 11.10. Because the 3-day default counts as a cooldown, the gate applies even in repositories with no cooldown key.

Renovate passes --before=<now minus minimumReleaseAge> to npm during lockfile generation. If that fails with ETARGET, Renovate retries without --before and logs a warning, so the transitive gate fails open. Its lockFileMaintenance updates, which re-resolve the whole tree, do not enforce minimumReleaseAge at all.

Our recommendation follows from the second clock: set the release-age gate in the package manager's own configuration, not only in the bot. A min-release-age=3 line in .npmrc, or minimumReleaseAge in pnpm-workspace.yaml, applies to every resolution: the bot's, a developer's npm install, and a CI job without a lockfile. Both bots keep the stricter of their own window and yours.

Security updates skip every clock

#

Both bots exempt security updates from the cooldown, because delaying a fix for a published vulnerability has a cost of its own. Dependabot goes further on npm. For security update jobs, its lockfile updater passes --min-release-age=0, which overrides the min-release-age in your .npmrc for that run. The change exists so that a fix published yesterday does not fail with ETARGET. The side effect is that every transitive dependency in a security fix pull request resolves with no age floor at all.

Dependabot vs Renovate on supply chain controls

#

Dependabot and Renovate solve the same problem, but their defaults differ on exactly the controls that matter when a release is malicious.

Control (October 2026)DependabotRenovate
HostingGitHub onlyGitHub, GitLab, Bitbucket, Azure DevOps, Gitea, Forgejo and others
Default release-age wait3 days for version updates since July 14, 2026None; config:best-practices adds 3 days for npm
Transitive dependenciesNative gate for npm 11.10+, pnpm and Yarn 4.10+ since August 2026; skipped if any package is excludednpm --before during lockfile generation, dropped on ETARGET; lockFileMaintenance ungated
Security updatesBypass the cooldown; npm age gate forced to 0Bypass minimumReleaseAge
Scripts while regenerating lockfilesIgnored for npm, Yarn and pnpmIgnored by default for npm, Bun and Composer (ignoreScripts: true)
CI secrets on bot pull requestsRead-only token, Dependabot secrets onlySame as any same-repository branch, including Actions secrets
Auto-mergeNone built in; needs a workflowOff by default; uses the platform's auto-merge when enabled
LicenseMIT (dependabot-core)AGPL-3.0

Our verdict: on GitHub, Dependabot is the safer default today, because it waits three days without configuration and keeps its CI runs away from your Actions secrets. Choose Renovate when you need another platform, monorepo-wide grouping, or fine-grained policy. If you do, turn on config:best-practices or set minimumReleaseAge, and audit which secrets your pull_request workflows expose. With either bot, set the release-age gate in the package manager too.

Is Dependabot free?

#

Dependabot is free on github.com for public and private repositories, including alerts, security updates and version updates. Its update engine, dependabot-core, is open source under the MIT license. The real costs are the CI minutes its pull requests trigger and the review time they consume. Renovate is also free: it is open source under the AGPL, and you can self-host it or install the hosted app.

Auto-merge trusts an identity attackers can borrow

#

Auto-merge workflows usually decide whether to trust a pull request by checking who sent it, and attackers have learned to borrow that identity. A common GitHub Actions pattern runs on pull_request_target, which has write access and secrets, and merges when github.actor is dependabot[bot]. But github.actor is whoever caused the latest event, not who wrote the pull request. Researchers at Synacktiv showed in 2024 that an attacker can open a pull request from a Dependabot branch in their own fork, then use a Dependabot command on the fork to make Dependabot force-push. Dependabot becomes the last actor, the check passes, and the workflow merges attacker code. Kong traced its December 2024 unauthorized Kong Ingress Controller 3.4.0 build to a "Pwn Request" exploit "enabled through a Dependabot actor confusion attack."

YAML
# Unsafe: github.actor is whoever triggered the latest event
if: github.actor == 'dependabot[bot]'

# Safer: the pull request's author, which a force-push does not change
if: github.event.pull_request.user.login == 'dependabot[bot]'

Checking the author is necessary but not sufficient. Run auto-merge on pull_request, not pull_request_target, and never check out the pull request's code in a privileged job. Our GitHub Actions security entry covers those triggers in depth.

Worms now impersonate the bot outright, because reviewers wave its pull requests through. After maintainer account takeovers, Mini Shai-Hulud pushed commits authored as dependabot[bot] without verified signatures, plus a workflow named dependabot/fix, into Intercom repositories during the Intercom compromise. It pushed a misspelled dependabot/fix-deds branch to Lightning-AI repositories, and in the Miasma Mini Shai-Hulud wave against LeoPlatform it planted a workflow named to look like Dependabot activity. A rule that trusts the bot's name, branch prefix or commit author is a rule the attacker can satisfy.

What we would change

#

The bots should keep picking versions. They are good at it, and stale dependencies carry real vulnerabilities. What has to change is who approves the pick.

First, put the cooldown in the package manager, so it binds every resolution, not only the bot's. Second, require a merge check that inspects the new version's contents rather than its metadata or the sender's identity. Socket for GitHub scans every pull request that changes a manifest or lockfile, bot pull requests included, and reports known malware and new install scripts in a comment. Marking its "Socket Security: Pull Request Alerts" check as required in branch protection makes GitHub's auto-merge wait on that result. A merge check cannot stop CI on the same branch from installing the package first. For that, run installs in CI through Socket Firewall, which blocks packages already identified as malicious before they download. Neither control catches a package nobody has analyzed yet. That gap is what the cooldown buys time to close.

The open problem is the exemption every bot shares. Security updates skip the cooldown, and on npm Dependabot also drops the transitive age floor for them. That makes a freshly published fix the one release that reaches repositories with no waiting period, from a maintainer account that may be exactly what an attacker controls. A Dependabot bug report shows how wide that path can get: a security update moved an already-safe direct dependency to its newest release, bypassing the cooldown. Closing that gap takes analysis of what the fix actually contains, not a longer clock.