From 27c4433939bc1ff1cf0d488d761dde0f5742eb46 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 13 Aug 2026 21:03:34 -0700 Subject: [PATCH] fix(ci): trust dependency approvers before graph compare (#123456) --- scripts/github/dependency-guard.mjs | 42 +++++++++---------- .../scripts/dependency-guard-workflow.test.ts | 4 +- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/scripts/github/dependency-guard.mjs b/scripts/github/dependency-guard.mjs index 18fcfa6c88c2..14980ecc0919 100644 --- a/scripts/github/dependency-guard.mjs +++ b/scripts/github/dependency-guard.mjs @@ -792,27 +792,6 @@ async function main() { return; } - const dependencyGraphChanges = await api.paginate( - `/repos/${owner}/${repo}/dependency-graph/compare/${pullRequest.base?.sha}...${pullRequest.head?.sha}`, - ); - if (isRemovalOnlyDependencyGraphChange(dependencyGraphChanges)) { - if (mode === "detect") { - await setOutput("autoscrub", "false"); - } - await upsertComment( - existingGuardComment, - renderRemovalOnlyDependencyComment({ - dependencyGraphChanges, - headSha: pullRequest.head?.sha, - }), - ); - await writeSummary( - "## Dependency Guard\n\nDependency removals are informational and do not require security approval.", - ); - console.log("Dependency removals detected; guard is informational."); - return; - } - const { isSecurityMember, isRepositoryAdmin } = createGuardApproverChecks({ api, owner, @@ -867,6 +846,27 @@ async function main() { return; } + const dependencyGraphChanges = await api.paginate( + `/repos/${owner}/${repo}/dependency-graph/compare/${pullRequest.base?.sha}...${pullRequest.head?.sha}`, + ); + if (isRemovalOnlyDependencyGraphChange(dependencyGraphChanges)) { + if (mode === "detect") { + await setOutput("autoscrub", "false"); + } + await upsertComment( + existingGuardComment, + renderRemovalOnlyDependencyComment({ + dependencyGraphChanges, + headSha: pullRequest.head?.sha, + }), + ); + await writeSummary( + "## Dependency Guard\n\nDependency removals are informational and do not require security approval.", + ); + console.log("Dependency removals detected; guard is informational."); + return; + } + const autoscrubCandidate = shouldAutoscrubDependencyLockfiles({ dependencyFiles, lockfileChanges, diff --git a/test/scripts/dependency-guard-workflow.test.ts b/test/scripts/dependency-guard-workflow.test.ts index 77db72efff65..14186d51e770 100644 --- a/test/scripts/dependency-guard-workflow.test.ts +++ b/test/scripts/dependency-guard-workflow.test.ts @@ -252,13 +252,15 @@ describe("dependency guard workflow", () => { expect(autoscrubCommentIndex).toBeGreaterThan(deleteCommentIndex); }); - it("checks trusted actors before autoscrub can mutate dependency changes", () => { + it("checks trusted actors before dependency graph comparison and autoscrub", () => { const script = readFileSync("scripts/github/dependency-guard.mjs", "utf8"); const trustedActorIndex = script.indexOf("const trustedActor ="); + const dependencyGraphCompareIndex = script.indexOf("const dependencyGraphChanges ="); const autoscrubCandidateIndex = script.indexOf("const autoscrubCandidate ="); const autoscrubOutputIndex = script.indexOf('await setOutput("autoscrub", "true")'); expect(trustedActorIndex).toBeGreaterThan(0); + expect(dependencyGraphCompareIndex).toBeGreaterThan(trustedActorIndex); expect(autoscrubCandidateIndex).toBeGreaterThan(trustedActorIndex); expect(autoscrubOutputIndex).toBeGreaterThan(trustedActorIndex); });