fix(ci): OSV suppression config actually loads — explicit global --config + expiring, reasoned ignores

The ignore file was inert from v1.65.0.0: OSV-Scanner only auto-discovers
configs named osv-scanner.toml (no leading dot) and applies them
per-directory, so the root config never covered lib/diagram-render/bun.lock
either way. The workflow now passes --config=.osv-scanner.toml globally.
Every IgnoredVulns entry carries a reason with an upgrade trigger and an
ignoreUntil expiry (~90 days) so suppressions must be re-justified. A wiring
test pins flag ↔ filename ↔ entry hygiene so the file can never silently go
inert again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-08-31 20:49:05 +00:00
co-authored by Claude Fable 5
parent 6258257cfc
commit 716196bba5
3 changed files with 89 additions and 0 deletions
+1
View File
@@ -21,6 +21,7 @@ jobs:
uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@f4cfcc01edc9c8b756a9b873b7a623ca674da51e # v2.3.8
with:
scan-args: |-
--config=.osv-scanner.toml
--include-git-root
--recursive
./
+41
View File
@@ -2,6 +2,19 @@
# Direct/transitive dependency versions are pinned to their fixed releases via
# the `overrides` block in package.json; this file only records advisories we
# have assessed as not-reachable or not-fixable without disproportionate risk.
#
# LOADING CONTRACT: OSV-Scanner only auto-discovers configs named
# osv-scanner.toml (no leading dot) and applies them per-directory — a root
# config never covers lib/diagram-render/bun.lock. The workflow therefore
# passes an explicit global `--config=.osv-scanner.toml`; the wiring test
# (test/osv-config-wiring.test.ts) pins that flag to this filename so the
# suppression file can never silently go inert again (it was inert from
# v1.65.0.0 to v1.78.0.0).
#
# Every entry carries `ignoreUntil` — suppressions expire and must be
# re-justified; the re-scan on expiry either finds a fix landed upstream or
# forces a fresh decision. Tracking issues are filed at ship time and named in
# each reason.
[[IgnoredVulns]]
id = "GHSA-frvp-7c67-39w9"
@@ -13,3 +26,31 @@ id = "GHSA-frvp-7c67-39w9"
# vulnerability we do not expose. Re-evaluate if the MCP SDK becomes a direct,
# server-hosting dependency.
reason = "Unreachable transitive (unused @modelcontextprotocol/sdk); fix requires a risky major override on a pinned peer dep."
ignoreUntil = 2026-11-30T00:00:00Z
[[IgnoredVulns]]
id = "GHSA-5p2g-fcmc-qvqq"
# image-size 1.2.1 via html-to-docx@1.8.0 (pins ^1.0.0). No fixed release
# exists (FIXED VERSION = --). Exposure: image-size only parses images the
# user themselves embeds into their own generated .docx — no untrusted input
# path. Upgrade trigger: an image-size release with a fix, or html-to-docx
# moving off it. Tracking issue filed at ship (v1.78.0.0 wave).
reason = "No fixed version exists; local-only input path (user's own docx images). Re-evaluate on expiry."
ignoreUntil = 2026-11-30T00:00:00Z
[[IgnoredVulns]]
id = "GHSA-w3rx-r6r6-pgpr"
# Same package/node as GHSA-5p2g-fcmc-qvqq above; same rationale.
reason = "No fixed version exists; local-only input path (user's own docx images). Re-evaluate on expiry."
ignoreUntil = 2026-11-30T00:00:00Z
[[IgnoredVulns]]
id = "GHSA-p7fg-763f-g4gf"
# @anthropic-ai/sdk 0.81.0 nested under @anthropic-ai/claude-agent-sdk@0.2.117,
# which is deliberately exact-pinned (eval-harness stability; the v1.77 wave
# pinned the whole harness after repeated CLI-drift breakage) and declares
# ^0.81.0 (0.x caret = 0.81.x only), so the 0.91.1 fix cannot be reached
# without violating the harness pin. 4.8 MEDIUM. Upgrade trigger: the next
# deliberate claude-agent-sdk bump. Tracking issue filed at ship (v1.78.0.0).
reason = "Fix requires breaking the deliberate eval-harness agent-sdk pin; MEDIUM severity accepted until the next harness bump."
ignoreUntil = 2026-11-30T00:00:00Z
+47
View File
@@ -0,0 +1,47 @@
/**
* OSV scanner config wiring (#2679-wave / v1.78.0.0).
*
* The suppression file was inert from v1.65.0.0 to v1.78.0.0: OSV-Scanner
* only auto-discovers configs named `osv-scanner.toml` (no leading dot) and
* applies them PER-DIRECTORY — a repo-root config never covers
* lib/diagram-render/bun.lock. The workflow must therefore pass an explicit
* global `--config` naming the file that actually exists. These tests pin
* that three-way agreement (workflow flag ↔ file on disk ↔ entry hygiene) so
* the filename and the flag can never drift apart silently again.
*/
import { describe, expect, test } from "bun:test";
import * as fs from "node:fs";
import * as path from "node:path";
const ROOT = path.resolve(import.meta.dir, "..");
const WORKFLOW = path.join(ROOT, ".github", "workflows", "osv-scanner.yml");
const CONFIG = path.join(ROOT, ".osv-scanner.toml");
describe("osv-scanner config wiring", () => {
test("workflow passes an explicit --config (auto-discovery never covers nested lockfiles)", () => {
const wf = fs.readFileSync(WORKFLOW, "utf-8");
const m = wf.match(/--config=(\S+)/);
expect(m).not.toBeNull();
// The flag must name a file that exists at repo root.
expect(fs.existsSync(path.join(ROOT, m![1]))).toBe(true);
});
test("every IgnoredVulns entry has id, reason, and an ignoreUntil expiry", () => {
const cfg = fs.readFileSync(CONFIG, "utf-8");
const entries = cfg.split("[[IgnoredVulns]]").slice(1);
expect(entries.length).toBeGreaterThan(0);
for (const entry of entries) {
expect(entry).toMatch(/^id = "GHSA-/m);
expect(entry).toMatch(/^reason = ".{20,}/m);
// Suppressions must expire — a permanent exception is a silent hole.
expect(entry).toMatch(/^ignoreUntil = \d{4}-\d{2}-\d{2}/m);
}
});
test("ignoreUntil dates are TOML datetimes the scanner can parse (not strings)", () => {
const cfg = fs.readFileSync(CONFIG, "utf-8");
// TOML datetime is unquoted; a quoted date silently parses as a string
// and (depending on scanner version) may be ignored.
expect(cfg).not.toMatch(/ignoreUntil = "/);
});
});