Renovate and Dependabot Security Configuration: Auto-Merge Boundaries and Scope Rules

Renovate and Dependabot Security Configuration: Auto-Merge Boundaries and Scope Rules

Problem

Dependency-update bots — Renovate, Dependabot — solve a real problem. Without them, dependencies stagnate; CVEs accumulate; teams spend half-days bumping versions before they can ship. With them, updates flow constantly.

The tension: every auto-merged update is a supply-chain decision. A compromised package, a typosquatted dependency, a maintainer-takeover release becomes an automatically-deployed update if the bot is configured to auto-merge.

The 2024-2025 incident landscape made this concrete: the xz backdoor, multiple npm typosquats with auto-malicious post-install scripts, the GitHub Actions registry compromises. Each could have been propagated by an auto-merging bot to hundreds of repositories.

By 2026 the bot-defaults are safer — Renovate’s minimumReleaseAge and Dependabot’s reviewer requirements provide friction — but production-safe configuration is more deliberate than what the docs default to.

The specific gaps in default bot configurations:

  • Auto-merge enabled for all minor/patch updates without review.
  • Major version bumps allowed automatically (often missed in policy).
  • Indirect dependencies (transitive) updated with the same automation.
  • No CVE-only auto-merge tier.
  • No release-age requirement (a brand-new release auto-merges within minutes).
  • No alignment with the org’s update SLAs.
  • Branch protection sometimes bypassed for the bot’s PRs.

This article covers Renovate (the more flexible of the two) and Dependabot configuration patterns: scope rules, minimum-release-age requirements, CVE-only auto-merge, sign-off and reviewer requirements, dependency-confusion mitigations, and the operational integration with code-owners.

Target systems: Renovate self-hosted or via GitHub App; Dependabot via GitHub native; concepts apply to GitLab Renovate, Snyk PR creator, etc.

Threat Model

  • Adversary 1 — Maintainer-takeover attack: legitimate package’s maintainer account compromised; a malicious release ships under the package’s name. Bot auto-merges into your repos.
  • Adversary 2 — Typosquat / dependency confusion: attacker publishes a package similar in name to one you depend on; misconfigured bot picks it up.
  • Adversary 3 — Malicious post-install script: new release contains code that runs during dependency install on your CI runners, exfiltrating secrets.
  • Adversary 4 — Major-version-bump abuse: bot auto-merges a major version that introduces silent breaking changes to security-relevant behavior.
  • Adversary 5 — Forged sign-off: the bot’s auto-approver mechanism is bypassed; updates merge without genuine review.
  • Access level: Adversaries 1-3 have malicious-package distribution capability. Adversary 4 is structural. Adversary 5 has CI-config-modify access.
  • Objective: Get malicious code into your build / runtime; cause subtle security regressions through unwanted updates.
  • Blast radius: with auto-merge enabled across an org, a single bad upstream release lands in every consumer repo simultaneously. With proper scope rules and release-age requirements, the same bad release is held back, observed, and either merged after vetting or skipped.

Configuration

Step 1: Renovate Base Configuration

// renovate.json — checked into the repo root.
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "config:recommended",
    ":dependencyDashboard"
  ],
  "labels": ["dependencies"],
  "automergeStrategy": "squash",
  "commitMessagePrefix": "chore(deps):",
  "rangeStrategy": "bump",
  "lockFileMaintenance": {
    "enabled": true,
    "schedule": ["before 5am on monday"]
  },
  "minimumReleaseAge": "7 days",
  "internalChecksFilter": "strict",
  "configMigration": true
}

Key settings:

  • minimumReleaseAge: 7 days — refuses updates to releases newer than 7 days. The xz backdoor was caught within ~3 days; this catches most recent compromises.
  • internalChecksFilter: strict — Renovate’s internal checks must pass; PRs aren’t created for known-stale or known-broken updates.
  • lockFileMaintenance confined to a weekly window.

Step 2: Per-Manager Scope Rules

Different package managers have different risk profiles. npm has the highest typosquat / supply-chain risk; Cargo has stronger publishing controls; system packages (apt) have distro maintainers.

{
  "packageRules": [
    {
      "matchManagers": ["npm"],
      "matchUpdateTypes": ["patch"],
      "automerge": true,
      "automergeType": "branch",
      "platformAutomerge": true,
      "minimumReleaseAge": "14 days"
    },
    {
      "matchManagers": ["npm"],
      "matchUpdateTypes": ["minor"],
      "automerge": false,
      "reviewers": ["team:security", "team:platform"]
    },
    {
      "matchManagers": ["npm"],
      "matchUpdateTypes": ["major"],
      "automerge": false,
      "reviewers": ["team:security"],
      "labels": ["dependencies", "major-version"]
    },
    {
      "matchManagers": ["cargo"],
      "matchUpdateTypes": ["patch", "minor"],
      "automerge": true,
      "minimumReleaseAge": "5 days"
    },
    {
      "matchManagers": ["dockerfile"],
      "matchUpdateTypes": ["pin", "digest"],
      "automerge": true,
      "minimumReleaseAge": "0 days"
    }
  ]
}

The rules:

  • npm patches: 14-day delay, auto-merge OK.
  • npm minor: requires security + platform review.
  • npm major: requires security review + explicit label.
  • Cargo patches/minors: 5-day delay (Cargo’s stricter publishing).
  • Dockerfile digest pins: auto-merge immediately (we control the pinning).

Step 3: Security-Tier Auto-Merge

Some updates fix CVEs; for those, bypass the standard delay (the CVE patch is more important than aging concerns).

{
  "packageRules": [
    {
      "matchUpdateTypes": ["patch", "minor"],
      "matchPackagePatterns": ["*"],
      "matchCurrentVersion": "!/^0/",
      "vulnerabilityAlerts": {
        "automerge": true,
        "labels": ["security", "vulnerability"],
        "minimumReleaseAge": "0 days",
        "schedule": ["at any time"]
      }
    }
  ]
}

A CVE-fixing patch auto-merges immediately. A non-CVE patch waits 7-14 days. The trade-off is intentional: known-bad-version-now > unknown-but-aging-version-later.

Step 4: Dependency Confusion Mitigation

{
  "packageRules": [
    {
      "matchManagers": ["npm"],
      "matchPackagePatterns": ["^@myorg/"],
      "registryUrls": ["https://npm.internal.example.com"],
      "automerge": false,
      "reviewers": ["team:platform"]
    },
    {
      "matchManagers": ["pip"],
      "matchPackagePatterns": ["^myorg-"],
      "registryUrls": ["https://pypi.internal.example.com"],
      "automerge": false
    }
  ]
}

Internal-namespace packages must come from the internal registry; never from the public registry. Pin via registryUrls.

For .npmrc enforcement at the build level:

# .npmrc
@myorg:registry=https://npm.internal.example.com
//npm.internal.example.com/:_authToken=${NPM_INTERNAL_TOKEN}

# Reject installation from any other registry by default.
registry=https://npm.internal.example.com

Combined: Renovate uses the right registry; the build-time install is also pinned. Dependency-confusion attacks (where a public registry has a package matching an internal name) fail at both.

Step 5: Reviewer Requirements

Bot PRs should require reviewers like any other PR. Renovate alone won’t bypass branch protection if your branch-protection rules require reviews.

# Branch protection in repo settings (or via Terraform GitHub).
required_pull_request_reviews:
  required_approving_review_count: 1
  require_code_owner_reviews: true
  dismiss_stale_reviews: true

# CODEOWNERS includes:
#   /package.json @myorg/security
#   /Cargo.toml @myorg/platform
#   /go.mod @myorg/platform

A code-owner from the security team reviews every dependency change. For auto-merging tiers, set up a bot-account that has CODEOWNERS approval rights but only on dependency files — and only triggers auto-merge after passing CI.

Step 6: Auto-Merge via Bot Account With Limited Scope

Don’t grant the same human-PAT auto-merge that a human has. Use a GitHub App or bot-account with scope to only merge dependency-related PRs:

# Branch protection allows the bot to bypass:
allowance_for_admins: false
allowance_for_specific_actors:
  - apps: [renovate-merge-bot]   # ONLY this bot can bypass

The Renovate bot account has no permission to push directly; it only merges PRs that pass CI and CODEOWNERS.

Step 7: Weekly / Monthly Aggregation

For low-velocity dependencies, aggregate updates rather than per-package PRs:

{
  "packageRules": [
    {
      "matchPackagePatterns": ["^@types/"],
      "groupName": "TypeScript types",
      "schedule": ["before 5am on monday"]
    },
    {
      "matchManagers": ["github-actions"],
      "groupName": "GitHub Actions",
      "schedule": ["before 5am on monday"]
    }
  ]
}

One PR with all type-package updates per week; one PR for GitHub Actions updates. Reviewers see a focused list rather than 30 individual PRs.

Step 8: Dependabot for GitHub Actions Specifically

Dependabot has good defaults for GitHub Actions specifically; complement Renovate (Renovate handles application deps; Dependabot can handle Actions).

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: weekly
    pull-request-branch-name:
      separator: "-"
    open-pull-requests-limit: 5
    reviewers:
      - "myorg/security-team"
    labels:
      - "dependencies"
      - "github-actions"
    commit-message:
      prefix: "ci"
      include: "scope"

GitHub Actions are themselves a supply-chain risk; treat the bot’s PRs as security-relevant. Pin actions to specific commits, not just tags:

# In workflow:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11   # v4.1.1

Tag-floats are risky; commit-pin-and-bot-update keeps you safe.

Step 9: Telemetry on Bot Activity

deps_pr_created_total{manager, type}
deps_pr_merged_total{manager, type, auto}
deps_pr_closed_unmerged_total{reason}
deps_security_vuln_detected_total{severity}
deps_security_vuln_fixed_total{severity, fix_age_seconds}

Alert on:

  • deps_security_vuln_detected_total{severity="critical"} rising — supply chain has lots of CVEs; investigate.
  • Long fix_age_seconds for critical vulns — bot stuck somewhere; investigate config.

Step 10: Periodic Audit

Quarterly:

  • Review PRs from the last 90 days; spot-audit auto-merged PRs.
  • Confirm CODEOWNERS still aligns with team responsibilities.
  • Update minimumReleaseAge based on observed incident response times.
  • Confirm no bypass routes (admin force-merge, etc.) were used.

Expected Behaviour

Signal Default bot config Hardened
New release published Auto-merged within hours Held until minimumReleaseAge (7-14 days)
Major version bump Often auto-merged Blocked; requires explicit review
CVE patch released Same delay as routine update Auto-merged immediately
Internal-namespace package from public registry Possibly merged Refused; registry pinned
Auto-merge bypass via admin Possible Blocked or alerted
GitHub Actions update with float tag Tag-only pin Commit-pin + bot maintains
Reviewer requirements Often skipped for bot PRs Required via CODEOWNERS

Trade-offs

Aspect Benefit Cost Mitigation
minimumReleaseAge Catches recent compromises Slower legitimate updates 7-14 days is reasonable; CVEs bypass for urgency.
Major-version review Avoids surprise breaking changes More PRs require human time Major bumps are rare; review effort proportional.
CVE fast-merge Fast security fix Depends on CVE detection accuracy Snyk / GitHub Dependabot / OSV scoring is generally reliable; fail-open on CVE detection (i.e., still apply the CVE patch).
Dependency-confusion pinning Defeats namespace squatting Internal registry must be operational Cache layer; failover to public is intentionally disabled.
Bot-account auto-merge Bot can’t be a backdoor for arbitrary code Bot-account scope to maintain Standard GitHub App pattern.
Quarterly audit Catches drift Time investment 1-2 hours per quarter.

Failure Modes

Failure Symptom Detection Recovery
minimumReleaseAge blocks legitimate fix Long-pending dep update Renovate dashboard shows aging PR If a legit security fix needs faster merge, manually merge with PR review.
CVE database miss Critical update treated as routine Vulnerability tracker shows your repo affected Subscribe to upstream security feeds; use GitHub Security Advisories.
Internal-namespace package mistakenly published to public Dependency-confusion vulnerability Public registry shows your package name Take down public publication; rotate any tokens published with it.
Auto-merge bot has too-broad permissions Bot can merge non-dependency PRs Audit log shows bot merging unexpected paths Tighten bot-account RBAC; restrict to dependency files only.
Reviewer subverted Code-owner team wins approval but reviewer was a bot account Audit shows reviews from automation Require human reviewer flag; refuse approvals from non-human accounts.
Dependency removal but bot keeps trying Confusing PRs for removed deps Renovate logs show errors Use ignoreDeps to explicitly skip; clean up old config.
Lock-file maintenance corrupts file Build fails after Monday merge CI reports broken lockfile Revert; investigate the specific package’s lockfile change; re-run.