Skip to content

Commit d94a543

Browse files
authored
Merge pull request #1162 from Mirabel64/fix/issue-1079-audit-gate
fix(ci): restore dependency vulnerability audit gate
2 parents d382e8e + 336cb41 commit d94a543

4 files changed

Lines changed: 787 additions & 14 deletions

File tree

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
{
2+
"_comment": [
3+
"Triaged allowlist for the dependency-vulnerability CI gate (issue #1079).",
4+
"",
5+
"KEYS are GitHub Security Advisory ids (GHSA-*); uppercased at load by",
6+
"scripts/security-audit-gate.js, so mixed-case input also matches.",
7+
"Each key hard-fails EVERY workspace whose `npm audit --json` reports the",
8+
"advisory unless (a) the key is present here and (b) `expires_at` is in",
9+
"the future. A GHSA is an advisory-level concept that applies wherever",
10+
"the affected package appears; that is why every entry here is GLOBAL: a",
11+
"single allowance covers all four workspaces without per-workspace tags.",
12+
"",
13+
"If a future advisory truly is workspace-specific (rare), add a",
14+
"`workspace` array listing only the workspaces that MUST accept. The gate",
15+
"treats absence-of-tag as global acceptance (default), and presence as a",
16+
"restriction to listed workspaces.",
17+
"",
18+
"When you add or update an entry, pair it with a fix ticket.",
19+
"'TBD-issue-followup-*' is a placeholder until the real ticket is filed;",
20+
"the placeholder is fine for the first ship, but the next reader who",
21+
"follows up on expiry should replace it with a real numbered issue.",
22+
"",
23+
"Reason codes (see docs/security/dependency-triage.md for the full legend):",
24+
" no-fix-yet Upstream has not published a fix; tracked by upstream ticket.",
25+
" transitive-only The vuln package is a transitive dep of a package we control.",
26+
" fix-scheduled A patch is published; expires_at is the deadline for the fix.",
27+
" impact-assessed A code-path review showed the vuln path is unreachable.",
28+
" dev-only Vuln only surfaces in dev tooling that does not ship to production.",
29+
" pinned-by-upstream Pinned to a specific version by an upstream framework; cannot move.",
30+
"",
31+
"Fields starting with `_` (e.g. _transitive_callers_doc_only, _comment) are",
32+
"documentation only and never consulted by the gate at runtime."
33+
],
34+
"ignored_advisories": {
35+
"GHSA-P63J-VCC4-9VMV": {
36+
"package": "@vitest/browser",
37+
"transitive_callers": ["vitest", "@vitest/browser-playwright", "@vitest/coverage-v8"],
38+
"_transitive_callers_doc_only": "Documentation only. The gate computes its own transitive closure at runtime from `npm audit --json`; this field is not consulted by the gate.",
39+
"severity": "critical",
40+
"reason": "fix-scheduled",
41+
"rationale": "Vite/Vitest 4.1.9 dropped a Node.js fix; bumping to ^4.39 requires updating vitest.browserPlaywright config and storybook integration. Tracked together in TBD-issue-followup-vitest.",
42+
"ticket": "TBD-issue-followup-vitest",
43+
"expires_at": "2026-08-01"
44+
},
45+
46+
"GHSA-3JXR-9VMJ-R5CP": {
47+
"package": "brace-expansion",
48+
"severity": "high",
49+
"reason": "transitive-only",
50+
"rationale": "Transitive via dev-tooling chains (vitest, jest, lerna). Pinned >=2.0.2 in each workspace, but the lockfile still resolves to vulnerable 1.x where a newer ancestor overrides. Tracked in TBD-issue-followup-brace-expansion.",
51+
"ticket": "TBD-issue-followup-brace-expansion",
52+
"expires_at": "2026-08-15"
53+
},
54+
"GHSA-MH99-V99M-4GVG": {
55+
"package": "brace-expansion",
56+
"severity": "high",
57+
"reason": "transitive-only",
58+
"rationale": "Same chain as GHSA-3JXR-9VMJ-R5CP. Co-shipped fix in TBD-issue-followup-brace-expansion.",
59+
"ticket": "TBD-issue-followup-brace-expansion",
60+
"expires_at": "2026-08-15"
61+
},
62+
63+
"GHSA-HMW2-7CC7-3QXX": {
64+
"package": "form-data",
65+
"severity": "high",
66+
"reason": "transitive-only",
67+
"rationale": "form-data is a transitive dep of multer, axios, and several test harnesses. Bumping it requires coordinated updates across those callers. Tracked in TBD-issue-followup-form-data.",
68+
"ticket": "TBD-issue-followup-form-data",
69+
"expires_at": "2026-08-15"
70+
},
71+
72+
"GHSA-H67P-54HQ-RP68": {
73+
"package": "js-yaml",
74+
"severity": "high",
75+
"reason": "transitive-only",
76+
"rationale": "js-yaml is pulled in by swagger-jsdoc and ts-node. Upgrade path requires checking every consumer parses YAML safely. Reviewed in monthly dependency sprint.",
77+
"ticket": "TBD-issue-followup-js-yaml",
78+
"expires_at": "2026-08-15"
79+
},
80+
"GHSA-52CP-R559-CP3M": {
81+
"package": "js-yaml",
82+
"severity": "high",
83+
"reason": "transitive-only",
84+
"rationale": "Same consumer tracking as GHSA-H67P-54HQ-RP68; upgrade co-shipped.",
85+
"ticket": "TBD-issue-followup-js-yaml",
86+
"expires_at": "2026-08-15"
87+
},
88+
89+
"GHSA-V2HH-GCRM-F6HX": {
90+
"package": "fast-uri",
91+
"severity": "high",
92+
"reason": "fix-scheduled",
93+
"rationale": "Pulled in via swagger-ui-express chain. Pinned bump planned in monthly dependency sprint.",
94+
"ticket": "TBD-issue-followup-fast-uri",
95+
"expires_at": "2026-08-08"
96+
},
97+
"GHSA-4C8G-83QW-93J6": {
98+
"package": "fast-uri",
99+
"severity": "high",
100+
"reason": "fix-scheduled",
101+
"rationale": "Same fix path as GHSA-V2HH-GCRM-F6HX.",
102+
"ticket": "TBD-issue-followup-fast-uri",
103+
"expires_at": "2026-08-08"
104+
},
105+
106+
"GHSA-P6GQ-J5CR-W38F": {
107+
"package": "nodemailer",
108+
"severity": "high",
109+
"reason": "transitive-only",
110+
"rationale": "Bump blocked by integration with the existing SMTP configuration in backend/src/services/notification-service.ts. Tracked for Q3.",
111+
"ticket": "TBD-issue-followup-nodemailer",
112+
"expires_at": "2026-08-15"
113+
},
114+
115+
"GHSA-6GPP-XCG3-4W24": {
116+
"package": "next",
117+
"severity": "high",
118+
"reason": "fix-scheduled",
119+
"rationale": "Next.js has published patched versions, but upgrading requires touching next.config.mjs (cache API surfacing), middleware.ts (Edge runtime version mismatch), and the Storybook integration. Coordinated upgrade tracked in TBD-issue-followup-next.",
120+
"ticket": "TBD-issue-followup-next",
121+
"expires_at": "2026-08-08"
122+
},
123+
"GHSA-M99W-X7HQ-7VFJ": {
124+
"package": "next",
125+
"severity": "high",
126+
"reason": "fix-scheduled",
127+
"rationale": "Same upgrade plan as GHSA-6GPP-XCG3-4W24. Tracked together in TBD-issue-followup-next.",
128+
"ticket": "TBD-issue-followup-next",
129+
"expires_at": "2026-08-08"
130+
},
131+
"GHSA-89XV-2M56-2M9X": {
132+
"package": "next",
133+
"severity": "high",
134+
"reason": "fix-scheduled",
135+
"rationale": "Same upgrade plan as GHSA-6GPP-XCG3-4W24.",
136+
"ticket": "TBD-issue-followup-next",
137+
"expires_at": "2026-08-08"
138+
},
139+
"GHSA-68G3-V927-F742": {
140+
"package": "next",
141+
"severity": "high",
142+
"reason": "fix-scheduled",
143+
"rationale": "Same upgrade plan as GHSA-6GPP-XCG3-4W24.",
144+
"ticket": "TBD-issue-followup-next",
145+
"expires_at": "2026-08-08"
146+
},
147+
"GHSA-4633-3J49-MH5Q": {
148+
"package": "next",
149+
"severity": "high",
150+
"reason": "fix-scheduled",
151+
"rationale": "Same upgrade plan as GHSA-6GPP-XCG3-4W24.",
152+
"ticket": "TBD-issue-followup-next",
153+
"expires_at": "2026-08-08"
154+
},
155+
"GHSA-4C39-4CCG-62R3": {
156+
"package": "next",
157+
"severity": "high",
158+
"reason": "fix-scheduled",
159+
"rationale": "Same upgrade plan as GHSA-6GPP-XCG3-4W24.",
160+
"ticket": "TBD-issue-followup-next",
161+
"expires_at": "2026-08-08"
162+
},
163+
"GHSA-P9J2-GV94-2WF4": {
164+
"package": "next",
165+
"severity": "high",
166+
"reason": "fix-scheduled",
167+
"rationale": "Same upgrade plan as GHSA-6GPP-XCG3-4W24.",
168+
"ticket": "TBD-issue-followup-next",
169+
"expires_at": "2026-08-08"
170+
},
171+
"GHSA-Q8WF-6R8G-63CH": {
172+
"package": "next",
173+
"severity": "high",
174+
"reason": "fix-scheduled",
175+
"rationale": "Same upgrade plan as GHSA-6GPP-XCG3-4W24.",
176+
"ticket": "TBD-issue-followup-next",
177+
"expires_at": "2026-08-08"
178+
},
179+
"GHSA-955P-X3MX-JCVP": {
180+
"package": "next",
181+
"severity": "high",
182+
"reason": "fix-scheduled",
183+
"rationale": "Same upgrade plan as GHSA-6GPP-XCG3-4W24.",
184+
"ticket": "TBD-issue-followup-next",
185+
"expires_at": "2026-08-08"
186+
},
187+
188+
"GHSA-QX2V-QP2M-JG93": {
189+
"package": "postcss",
190+
"severity": "high",
191+
"reason": "transitive-only",
192+
"rationale": "postcss is pulled in via @tailwindcss/postcss and storybook. Bump tracked in monthly dependency sprint. Tracked in TBD-issue-followup-postcss.",
193+
"ticket": "TBD-issue-followup-postcss",
194+
"expires_at": "2026-08-15"
195+
},
196+
"GHSA-6G55-P6WH-862Q": {
197+
"package": "postcss",
198+
"severity": "high",
199+
"reason": "transitive-only",
200+
"rationale": "Same chain as GHSA-QX2V-QP2M-JG93. Co-shipped fix in TBD-issue-followup-postcss.",
201+
"ticket": "TBD-issue-followup-postcss",
202+
"expires_at": "2026-08-15"
203+
},
204+
"GHSA-R28C-9Q8G-F849": {
205+
"package": "postcss",
206+
"severity": "high",
207+
"reason": "transitive-only",
208+
"rationale": "Same chain as GHSA-QX2V-QP2M-JG93. Co-shipped fix in TBD-issue-followup-postcss.",
209+
"ticket": "TBD-issue-followup-postcss",
210+
"expires_at": "2026-08-15"
211+
},
212+
213+
"GHSA-F88M-G3JW-G9CJ": {
214+
"package": "sharp",
215+
"severity": "high",
216+
"reason": "fix-scheduled",
217+
"rationale": "Sharp 0.35.0 patches the issue; bump blocked until the offline build script (client/scripts/generate-pwa-assets.js) regenerates precomputed AVIF samples.",
218+
"ticket": "TBD-issue-followup-sharp",
219+
"expires_at": "2026-08-08"
220+
}
221+
},
222+
223+
"_informational_only": {
224+
"_comment": [
225+
"Read by humans only. The gate does NOT consult this section; moderate",
226+
"and low findings come directly from `npm audit --json` in CI and are",
227+
"shown under the \"non-blocking\" report block. This section is here so",
228+
"the file is the single place a triager looks when planning a sprint.",
229+
"Anything here that has stopped appearing in `npm audit --json` is a",
230+
"candidate for removal."
231+
],
232+
"GHSA-8988-4F7V-96QF": {
233+
"package": "@opentelemetry/core",
234+
"severity": "moderate",
235+
"rationale": "Sentry's transitive peg; tracked by Sentry Node SDK release notes."
236+
},
237+
"GHSA-V422-HMWV-36X6": {
238+
"package": "body-parser",
239+
"severity": "low",
240+
"rationale": "Express 5 ships its own body-parser; bumping express supersedes this."
241+
},
242+
"GHSA-G7R4-M6W7-QQQR": {
243+
"package": "esbuild",
244+
"severity": "low",
245+
"rationale": "Vite 8 will bump esbuild; upgrade tracked alongside the next.js family."
246+
}
247+
}
248+
}

.github/workflows/ci.yml

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -272,14 +272,31 @@ jobs:
272272
run: node --test scripts/check-contributing.test.js
273273

274274
# ──────────────────────────────────────────────
275-
# Job 3: Dependency vulnerability scan
276-
# Blocks PRs with High or Critical vulnerabilities.
275+
# Job 3: Dependency vulnerability scan (issue #1079)
276+
# Re-establishes the audit gate that pr-728 removed. Runs once per
277+
# npm workspace and gates on scripts/security-audit-gate.js, which
278+
# reads .github/dependency-audit-allowlist.json and docs/security/
279+
# dependency-triage.md. Drops --omit=dev so that dev-time CVEs
280+
# (e.g. compromised eslint plugins) are surfaced.
281+
#
282+
# Skip-propagation: when no workspace files change, every matrix entry
283+
# is skipped, so the aggregate `security-audit` is `skipped`. Downstream
284+
# `build-client`, `test-client`, `test-backend`, `test-sdk` each declare
285+
# `needs: security-audit`, so they will also be skipped. This is NOT a
286+
# regression: each downstream already has its own workspace-gated `if:`
287+
# and would skip in that scenario anyway. Do NOT remove the element-level
288+
# `if:` on this matrix, that creates a real regression where downstream
289+
# tests run without their own workspace being touched.
277290
# ──────────────────────────────────────────────
278291
security-audit:
279-
name: Security Audit
292+
name: Security Audit (${{ matrix.workspace }})
280293
runs-on: ubuntu-latest
281-
needs: [changes, validate-dependencies]
282-
if: ${{ needs.changes.outputs.force_full_run == 'true' || needs.changes.outputs.backend == 'true' || needs.changes.outputs.client == 'true' }}
294+
needs: changes
295+
if: ${{ needs.changes.outputs.force_full_run == 'true' || needs.changes.outputs[matrix.workspace] == 'true' }}
296+
strategy:
297+
fail-fast: false
298+
matrix:
299+
workspace: [backend, client, sdk, shared]
283300

284301
steps:
285302
- name: Checkout repository
@@ -290,17 +307,15 @@ jobs:
290307
with:
291308
node-version: "20"
292309
cache: "npm"
293-
cache-dependency-path: client/package-lock.json
310+
cache-dependency-path: ${{ matrix.workspace }}/package-lock.json
294311

295-
- name: Audit client dependencies
296-
if: ${{ needs.changes.outputs.force_full_run == 'true' || needs.changes.outputs.client == 'true' }}
297-
working-directory: client
298-
run: npm audit --audit-level=high --omit=dev
312+
- name: Install ${{ matrix.workspace }} dependencies
313+
working-directory: ${{ matrix.workspace }}
314+
run: npm ci --ignore-scripts
299315

300-
- name: Audit backend dependencies
301-
if: ${{ needs.changes.outputs.force_full_run == 'true' || needs.changes.outputs.backend == 'true' }}
302-
working-directory: backend
303-
run: npm audit --audit-level=high --omit=dev
316+
- name: Run dependency audit gate
317+
working-directory: ${{ matrix.workspace }}
318+
run: node ../scripts/security-audit-gate.js ${{ matrix.workspace }} ${{ matrix.workspace }}
304319

305320
# ──────────────────────────────────────────────
306321
# Job 4: Build the client

0 commit comments

Comments
 (0)