Repository navigation
Quarantine new dependency releases while raising security advisories immediately #3137
Description
Summary
Automated dependency updates currently have 2 opposite problems, and fixing the first one on its own makes the second one worse.
Routine updates are adopted the moment a release is published. That is exactly the window in which a compromised package release is live and not yet yanked - the pattern behind recent package-registry supply-chain incidents, where malicious versions were published and pulled within hours.
Security fixes, meanwhile, are adopted too slowly, because Renovate can't see the advisories. Renovate already has a fast path for advisory-driven updates: its built-in vulnerabilityAlerts defaults skip the group schedules, the concurrent pull request limit and any minimum release age. That path only opens for advisories Renovate can read, which on GitHub means Dependabot alerts that are enabled and readable by the Renovate app or token. Dependabot alerts are disabled on this repository, so every fix arrives as an ordinary update, inherits its group's schedule, and can wait up to a week after the advisory is published before a pull request appears.
The fix has 2 halves: quarantine new releases for a minimum age, and make advisories visible to Renovate so their fixes take the exempt path and are raised immediately. A quarantine on its own adds days to every fix Renovate can't see, so it can't land without the visibility half. Visible advisories on their own are safe to land, but they leave the compromised-release window wide open.
At a glance
A ROUTINE RELEASE THAT TURNS OUT TO BE COMPROMISED (PUBLISHED ON DAY 0)
day 0 1 2 3 4 5 6 7
├──────┼──────┼──────┼──────┼──────┼──────┼──────┤
registry ├┤ live for hours, then pulled
today * PR can open in that window; the Actions group automerges
proposed ├─ 3-day quarantine ─┤ gone before it can be proposed
A SECURITY FIX IN A WEEKLY GROUP (ADVISORY PUBLISHED ON DAY 0)
day 0 1 2 3 4 5 6 7
├──────┼──────┼──────┼──────┼──────┼──────┼──────┤
today ├───── up to 7 days, until Sunday's window ──────┤ PR
advisory not visible to Renovate: the fix is a routine update
proposed * PR on the next run
advisory visible to Renovate: its built-in exemption applies
WHAT EACH HALF FIXES ON ITS OWN
routine release security fix
is proposed at is proposed after
───────────────────── ─────────────────────
today any age exposed up to 7 days slow
quarantine only 3+ days old ok up to 10 days slower
visible advisories only any age exposed the next run ok
both halves 3+ days old ok the next run ok
Details
renovate.json puts every PHP package behind before 2am on Sunday, Drupal core behind before 2am, JavaScript packages behind before 2am on Sunday, and container images and Actions behind before 3am. There's no minimum release age and no vulnerabilityAlerts block. The missing block isn't what slows fixes down, since Renovate's built-in defaults already exempt advisory-driven updates. Dependabot alerts are disabled on this repository, so Renovate never receives an advisory to act on. The GitHub Actions group is configured with automerge: true, so an Actions release can be adopted and merged automatically within minutes of publication with no human in the loop.
The security audit job already tells a project it is exposed - composer audit --locked fails the build as soon as an advisory lands against a resolved dependency. What's missing is the other half: Renovate never sees the advisory the audit just reported, so the remedy doesn't start until the group's schedule allows it. The result is a repository that knows it is vulnerable and then waits up to a week for the bot to offer the fix, or has someone bump it by hand.
Setting a minimum release age changes the risk profile cheaply. Routine updates arrive a few days later than they do today, and releases that are pulled, yanked or superseded inside that window never reach the project at all. The cost is bounded and predictable; the benefit is that the project is never the first consumer of a compromised release.
Proposed implementation
1. Add the quarantine as a top-level setting
A top-level minimumReleaseAge in renovate.json applies to every manager at once, so no per-group rules need touching and the existing grouping and schedules stay exactly as they are:
"minimumReleaseAge": "3 days",Three days is the proposal, not a fixed requirement: long enough to cover the observed window in which malicious releases were pulled, short enough that a weekly grouped update is unaffected in practice, since the group only runs once a week anyway. For the container-image and GitHub Actions groups, which run nightly, this is the only group where the delay is actually visible.
2. Rely on the built-in advisory exemption, and label it
vulnerabilityAlerts is the config object Renovate applies to pull requests raised in response to an advisory, and its built-in defaults are already the exemption this change needs: schedule: [], minimumReleaseAge: null and prConcurrentLimit: 0. Renovate's docs also state that schedule is always ignored for these pull requests and can't be overridden. So once Renovate can see an advisory, its fix already skips the group windows, the quarantine from section 1 and the routine prConcurrentLimit: 10 queue, with nothing added to renovate.json.
The one addition worth making is the label:
"vulnerabilityAlerts": {
"labels": ["Dependencies", "Security"]
},labelsreuses the repository's existingSecuritylabel, so advisory pull requests are distinguishable at a glance with no new label to create.enabled,minimumReleaseAge,scheduleandprConcurrentLimitstay out, since setting them would only restate the defaults.prPrioritystays out too: advisory pull requests aren't held by the routine limit, so there's no queue for them to jump.
3. Make advisories visible to Renovate
This is the half that changes behaviour, and it needs a decision rather than a config line. Renovate can learn about advisories 2 ways, and the template supports 2 Renovate deployment modes through the installer's dependency-updates provider handler, which behave differently:
- Hosted app (
renovatebot_app): reads the repository's Dependabot alerts, provided the dependency graph and Dependabot alerts are enabled in the repository settings and the app has read access to Dependabot alerts. Dependabot alerts are disabled on this repository, so the app currently sees no advisories. - Self-hosted via CI (
renovatebot_ci): runs from.github/workflows/update-dependencies.ymlwithtoken: ${{ secrets.RENOVATE_TOKEN }}. The same repository settings apply, and that token must be able to read the repository's Dependabot alerts, orvulnerabilityAlertssilently does nothing and the feature appears to work while doing exactly nothing. The settings and the token permission requirement have to be documented as part of this change.
osvVulnerabilityAlerts: true is the fallback when Dependabot alerts can't be read, since it sources advisories from the OSV database instead. It's flagged experimental upstream and only covers direct dependencies, so it should be evaluated rather than adopted blindly.
4. Make quarantined updates visible
With a quarantine in place, an update that is waiting out its window is reported as pending in the Dependency Dashboard. The self-hosted workflow sets RENOVATE_DEPENDENCY_DASHBOARD: ${{ vars.RENOVATE_DEPENDENCY_DASHBOARD || 'false' }}, so the dashboard is off unless a project opts in - which means quarantined updates would be invisible by default and the delay would look like the bot having stopped working.
Either the dashboard default flips for projects using the quarantine, or the documentation states plainly that the dashboard is how pending updates are seen. This needs deciding as part of the change, not after someone reports the bot as broken.
5. Verify with a dry run before merging
The workflow already supports this: set the RENOVATE_DRY_RUN repository variable to true and inspect the run log. Renovate's docs and defaults already settle how the exemption behaves, so the run is for the 2 things config can't show: that Renovate actually reads advisories in each deployment mode, and that routine updates younger than the window are held back. Both should be confirmed against a real run rather than assumed.
Scope limitation worth knowing up front
This repository does not commit a root composer.lock. Advisory detection works from a resolved dependency graph, so the template repository itself will get little or nothing from the PHP side of this - the alerts have nothing precise to match against. Consumer projects, which do commit a lock file, are where the benefit actually lands.
The practical consequence is that this cannot be fully validated on the template and needs checking against a consumer project. It also means the JavaScript side, which does commit package-lock.json, is the only part observable here, and only once Dependabot alerts are enabled.
Acceptance criteria
- A top-level minimum release age is configured, so a release is not proposed until it has been published for at least that long.
- The chosen window is documented with the reasoning, so projects can lengthen or shorten it deliberately.
- Updates raised in response to a published security advisory are exempt from the quarantine and are not subject to the group schedules, appearing as soon as the advisory is available.
- Advisory-driven pull requests carry the existing
Securitylabel so they are distinguishable from routine grouped updates. - An advisory fix is not blocked by the routine concurrent-pull-request limit.
- Existing grouping and schedules for routine updates are unchanged.
- Automerged groups remain automerged, but only for releases that have cleared the quarantine window.
- Renovate can read advisories in both the hosted and the self-hosted mode, through Dependabot alerts or the OSV fallback, and the repository settings and self-hosted token permission this needs are documented.
- Pending quarantined updates are discoverable, either by the dashboard default or by documentation that states how to see them.
- The quarantine doesn't land before advisory visibility, since on its own it adds days to every fix Renovate can't see.
- The behaviour is verified with a dry run, confirming that Renovate reads advisories and that routine updates inside the window are held back.
- The configuration passes the existing Renovate config validation step in the dependency-update workflow.
- Installer fixtures and snapshots are updated, since
renovate.jsonis processed by the installer's dependency-updates provider handler and shipped to consumers. - The documentation covers the interaction between the quarantine, the advisory exemption and the update schedules, and how a project turns either half off if its policy requires batching.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNext release
Activity
[-]Raise dependency security advisories immediately instead of on the grouped schedule[/-][+]Quarantine new dependency releases while raising security advisories immediately[/+]