From bc47fcbcf0485dcf600fdb399be8ae78ec3015b8 Mon Sep 17 00:00:00 2001 From: cliffhall Date: Fri, 4 Sep 2026 08:22:18 -0400 Subject: [PATCH] chore(deps): address Copilot review round 12 on #2243 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build the open-GHSA set from the raw alert feed, not from the grouped alerts. groupAlerts deliberately drops an alert with no first_patched_version, so an advisory that stays OPEN but loses its patched version vanished from both the keys and that set — and reconciliation would have called it "fixed or dismissed". Same wrong direction as the superseded case fixed last round, reached a different way: a still-open advisory must never stand itself down. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_013H6Cew3KB3jVmQ5x4Dq1sq Signed-off-by: cliffhall --- scripts/dependabot-alerts.mjs | 22 ++++++++++++++++--- scripts/dependabot-alerts.test.mjs | 35 ++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/scripts/dependabot-alerts.mjs b/scripts/dependabot-alerts.mjs index 8f3159f0..4d388bc6 100644 --- a/scripts/dependabot-alerts.mjs +++ b/scripts/dependabot-alerts.mjs @@ -1019,7 +1019,8 @@ export function main( const securityPrsOff = checkSecurityPrsStillDisabled(repo, spawn); - const groups = groupAlerts(openAlerts(repo, spawn)); + const alerts = openAlerts(repo, spawn); + const groups = groupAlerts(alerts); // ⚠️ Loaded BEFORE the zero-group early return, and reconciled after the loop. // `openAlerts` asks for `state=open`, so an alert that is FIXED or DISMISSED @@ -1036,8 +1037,23 @@ export function main( const boardProblems = []; /** Grouping keys this run actually saw in the open feed. */ const seenKeys = new Set(); - /** Every GHSA still open, in ANY group — the check a vanished key needs. */ - const openGhsas = new Set(groups.flatMap((g) => g.ghsas)); + /** + * Every GHSA still open, taken from the RAW feed rather than from `groups`. + * + * ⚠️ `groupAlerts` deliberately drops an alert with no `first_patched_version` + * — there is nothing to bump to, so nothing to file. Building this set from + * the groups would inherit that filter, so an advisory that stays open but + * LOSES its patched version would vanish from both the keys and this set, and + * reconciliation would call it "fixed or dismissed" (Copilot). Same wrong + * direction as the superseded case, reached a different way: what a still-open + * advisory must never do is stand itself down. + */ + const openGhsas = new Set( + alerts + .filter((a) => a.state === "open") + .map((a) => a.security_advisory?.ghsa_id) + .filter(Boolean), + ); for (const rawGroup of groups) { seenKeys.add(rawGroup.key); diff --git a/scripts/dependabot-alerts.test.mjs b/scripts/dependabot-alerts.test.mjs index f8325b22..499ff580 100644 --- a/scripts/dependabot-alerts.test.mjs +++ b/scripts/dependabot-alerts.test.mjs @@ -1415,6 +1415,41 @@ test("main omits the security-PR claim when the token could not read it", () => ); }); +test("main will not stand down an open alert that lost its patched version", () => { + // groupAlerts drops an alert with no first_patched_version, so the bump's key + // disappears — but the advisory is still OPEN. Reading the open-GHSA set from + // the groups instead of the raw feed would report it as fixed or dismissed. + const [filed] = groupAlerts([alert({ ghsa: "GHSA-a" })]); + const unpatched = alert({ ghsa: "GHSA-a" }); + unpatched.security_vulnerability.first_patched_version = null; + + const spawn = fakeSpawn({ + alertPages: [[unpatched]], + issues: [ + { + number: 41, + title: buildIssueTitle(filed), + body: buildIssueBody(filed, asInstalled()), + }, + ], + }); + inTempRepo({ "package-lock.json": lockWith("fast-uri", "3.1.5") }, () => + withoutProjectToken(() => + captureLog(() => main("o/r", spawn, "2026-09-04")), + ), + ); + const edit = ghCall(spawn, "edit"); + assert.ok(edit, "the issue is still reconciled"); + const body = edit.args[edit.args.indexOf("--body") + 1]; + assert.doesNotMatch( + body, + /fixed or dismissed/, + "a still-open advisory must never stand itself down", + ); + assert.match(body, /superseded/); + assert.match(body, /`GHSA-a` is still open/); +}); + test("main reads every page of open dependabot issues", () => { // The second page holds the matching marker. Truncating the lookup would // file a duplicate issue rather than recognising this one.