From 7b07472ad2d9d12da9ba81221b5d212c45f1af7f Mon Sep 17 00:00:00 2001 From: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com> Date: Thu, 29 Oct 2026 23:50:17 -0400 Subject: [PATCH 1/2] fix: npm audit gate documents, matches and reports package-scoped exceptions Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com> --- changelog.d/fixed-523-npm-audit-gate-package-scope.md | 3 +++ scripts/npm-audit-gate.test.ts | 8 +++++++- scripts/npm-audit-gate.ts | 11 +++++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 changelog.d/fixed-523-npm-audit-gate-package-scope.md diff --git a/changelog.d/fixed-523-npm-audit-gate-package-scope.md b/changelog.d/fixed-523-npm-audit-gate-package-scope.md new file mode 100644 index 00000000..c50c1f08 --- /dev/null +++ b/changelog.d/fixed-523-npm-audit-gate-package-scope.md @@ -0,0 +1,3 @@ +- The npm audit gate documents the optional `package` exception field, treats an + empty `package` as unscoped, and names the package in unused-exception + warnings (hivecommons/docs#523, #524, #525). diff --git a/scripts/npm-audit-gate.test.ts b/scripts/npm-audit-gate.test.ts index 6871937e..3d8bf9c2 100644 --- a/scripts/npm-audit-gate.test.ts +++ b/scripts/npm-audit-gate.test.ts @@ -184,6 +184,12 @@ describe("evaluateAudit", () => { expect(r.blocking).toEqual([]); }); + it("treats an empty package as an unscoped exception", () => { + const r = evaluateAudit(REPORT, [exception({ package: "" })], "high", NOW); + expect(r.blocking).toEqual([]); + expect(r.unused).toEqual([]); + }); + it("does not apply an exception scoped to a different package", () => { const r = evaluateAudit( REPORT, @@ -416,7 +422,7 @@ describe("main", () => { expect(exitSpy).not.toHaveBeenCalled(); const out = logSpy.mock.calls.flat().join("\n"); expect(out).toMatch(/Acknowledged 1 advisory/); - expect(out).toMatch(/::warning::Unused npm audit exception GHSA-zzzz/); + expect(out).toMatch(/::warning::Unused npm audit exception GHSA-zzzz-zzzz-zzzz \(nope\)/); expect(out).toMatch(/Ignoring 1 advisory/); expect(out).toMatch(/passed at level "high"/); }); diff --git a/scripts/npm-audit-gate.ts b/scripts/npm-audit-gate.ts index 93e5740a..4ba071db 100644 --- a/scripts/npm-audit-gate.ts +++ b/scripts/npm-audit-gate.ts @@ -12,6 +12,11 @@ * expired exception fails the gate so acknowledged findings are re-reviewed * rather than silently forgotten. * + * An exception may also carry an optional `package` that scopes it to one + * affected package, so one GHSA can be listed once per package. Without + * `package` (or with an empty one) the exception covers that GHSA for every + * package. + * * A file that is not an audit report (e.g. the error document npm writes when * the registry is unreachable) fails the gate rather than passing it. * @@ -124,7 +129,7 @@ export interface GateResult { } function exceptionKey(id: string, pkg?: string): string { - return `${id.toUpperCase()}:${pkg ?? "*"}`; + return `${id.toUpperCase()}:${pkg || "*"}`; } export function evaluateAudit( @@ -240,7 +245,9 @@ export function main(argv: string[] = process.argv.slice(2)): void { ); } for (const ex of result.unused) { - console.log(`::warning::Unused npm audit exception ${ex.id} — remove it.`); + console.log( + `::warning::Unused npm audit exception ${ex.id} (${ex.package || "*"}) — remove it.` + ); } let failed = false; From df7457177c519a38b809261b9dc79dde64b888a8 Mon Sep 17 00:00:00 2001 From: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com> Date: Fri, 30 Oct 2026 00:17:45 -0400 Subject: [PATCH 2/2] style: format npm-audit-gate test for prettier Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com> --- scripts/npm-audit-gate.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/npm-audit-gate.test.ts b/scripts/npm-audit-gate.test.ts index 3d8bf9c2..61d96a37 100644 --- a/scripts/npm-audit-gate.test.ts +++ b/scripts/npm-audit-gate.test.ts @@ -422,7 +422,9 @@ describe("main", () => { expect(exitSpy).not.toHaveBeenCalled(); const out = logSpy.mock.calls.flat().join("\n"); expect(out).toMatch(/Acknowledged 1 advisory/); - expect(out).toMatch(/::warning::Unused npm audit exception GHSA-zzzz-zzzz-zzzz \(nope\)/); + expect(out).toMatch( + /::warning::Unused npm audit exception GHSA-zzzz-zzzz-zzzz \(nope\)/ + ); expect(out).toMatch(/Ignoring 1 advisory/); expect(out).toMatch(/passed at level "high"/); });