Skip to content

Workflow: consume Dependabot security alerts into board-tracked issues and disable security-update PRs #2233

Description

@cliffhall

Sub-issue of #2229 — the alert-consuming half. (#2231 covers the other half: npm audit fix at release time and the monthly npm outdated issue.)

Turn Dependabot security-update PRs off. Keep its alerts on. Drive our own issues from the alerts, and write the fix ourselves against v2/main.

Dependabot alert  →  scheduled sweep  →  issue (labeled, milestoned, boarded)  →  maintainer PR → v2/main

Verified mechanics

Each of these was checked against this repo, because the approach rests on them:

Claim Status
Alerts are readable from a workflow with no PAT GITHUB_TOKEN supports vulnerability-alerts: read (workflow syntax). Read-only, which is all we need.
There is no dependabot_alert workflow trigger ✅ Webhook event only, not in the events reference. So this is a scheduled sweep, not event-driven.
PRs can be disabled while alerts stay on ✅ Two independent toggles. GET /repos/{o}/{r}/automated-security-fixes currently returns {"enabled":true,"paused":false}; DELETE on that path turns the PRs off. GET /repos/{o}/{r}/vulnerability-alerts returns 204 and is unaffected.
Alerts carry everything an issue needs dependency.package.name, dependency.manifest_path, dependency.scope, security_advisory.{ghsa_id,cve_id,severity,summary}, security_vulnerability.{vulnerable_version_range,first_patched_version.identifier}.
semver is already resolvable at the repo root ✅ Needed for the range check below; no new dependency.

Group alerts by bump, not by advisory

Alerts are per-advisory. At the time of writing, 7 open alerts across 3 packages: fast-uri ×4 (package-lock.json, high, fixed in 3.1.6), qs ×2 (package-lock.json, medium, fixed in 6.16.0), browserslist ×1 (clients/tui/package-lock.json, high/dev, fixed in 4.28.7). A 1:1 mapping would file seven issues for three overrides entries. So:

  • Group by (package, manifest_path, first_patched_version) — one issue per bump, listing every GHSA it clears.
  • The issue body carries a machine-readable marker naming every GHSA covered (<!-- dependabot-alerts: GHSA-…,GHSA-… -->). That marker is the idempotency key.
  • A new alert for a package with an open issue gets a comment on that issue, not a second issue, and the marker is rewritten to include it.

Verify against v2/main before filing

GitHub computes the dependency graph, and therefore every alert, from the default branch (main). The sweep does not trust the alert's applicability — it re-checks the vulnerable range against v2/main's own lockfile before filing:

git show origin/v2/main:<manifest_path>   # resolve the installed version
# then: semver.satisfies(installed, alert.security_vulnerability.vulnerable_version_range)
  • Satisfies → real on the branch we ship from. File.
  • Does not satisfy → already fixed on v2/main, alert is waiting on a milestone merge to close. Skip silently.
  • Manifest absent on v2/main → log and skip.

Run today, this confirms live exposure: origin/v2/main carries fast-uri@3.1.5 and qs@6.15.3, both in range.

Blind spot, stated plainly: a vulnerable dependency introduced on v2/main and not yet merged to main produces no alert at all. No approach that consumes GitHub's alerts avoids this. The release-time npm audit fix from #2231 is the second signal that partially covers it; a scheduled npm audit --audit-level=high over v2/main's lockfiles would close it fully and is a separable follow-up.

Schedule and permissions

on:
  schedule: [{ cron: "17 6 * * *" }]   # daily; alerts are not minute-sensitive
  workflow_dispatch:
permissions:
  contents: read
  issues: write
  vulnerability-alerts: read

Daily, not hourly — with Dependabot's PRs off there is no mergeable-against-main artifact and no window to race, so #2060's merge guard is unnecessary.

The only thing GITHUB_TOKEN cannot do is write the board card (org project → fine-grained PAT or App token with organization projects: write). Degrade gracefully: if the project write fails or the secret is absent, still create the issue with labels and milestone and let the next /issue-triage sweep board it — its documented exception moves an unboarded-but-milestoned issue straight into Todo. The PAT is an optimization, not a prerequisite.

Issue shape

Field Value
Title chore(deps): bump `<pkg>` to `<first_patched>` in `<manifest>` (<n> advisories)
Labels v2, chore, dependabot
Milestone current open milestone with the nearest due date, resolved at runtime
Board #28, Status Todo, Priority High

Todo, not Incoming — arriving through this pipeline is the approval. Priority High is a standing rubric override (a routine bump scores Medium; these must not sit). Record the override in the body.

The body states, from the alert plus the lockfile probe: every GHSA / CVE with severity and summary; installed version on v2/main and the version to reach; runtime vs development scope; direct vs transitive — a transitive fix is an overrides entry per Dependency placement, never npm audit fix, and all three of today's packages are transitive; links to the alert pages.

Turning security PRs off

DELETE /repos/modelcontextprotocol/inspector/automated-security-fixes (or Settings → Code security). Because it is a repo setting, it can be re-enabled from the UI without a commit — so the sweep asserts GET …/automated-security-fixes still reports disabled and fails loudly if not. That is this design's analogue of #2060's merge guard: one API call instead of a required status check plus a ruleset change.

Acceptance

  • Dependabot opens no security PRs; automated-security-fixes reports disabled and vulnerability-alerts reports enabled.
  • The sweep files one issue per bump, not per advisory: today's 7 alerts produce exactly 3 issues, each naming every GHSA it clears.
  • An alert whose vulnerable range no longer matches v2/main's lockfile produces no issue.
  • Each issue is labeled v2 + chore + dependabot, carries the current milestone, and sits on board Add tab and approval flow for server -> client sampling #28 in Todo at Priority High — and when the project write is unavailable, it is still created labeled and milestoned.
  • A second run the same day is a complete no-op.
  • A new advisory for a package with an open issue lands as a comment, not a new issue.
  • The run fails loudly if Dependabot security PRs have been re-enabled.
  • AGENTS.md and .github/dependabot.yml's comment block describe the alert-driven flow.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

choreMaintenance: deps, build tooling, CI, cleanup — no user-facing behavior changedependabotv2Issues and PRs for v2

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions