From c06c12302f252c9de51e293b8b45320378856c4b Mon Sep 17 00:00:00 2001 From: Aric Camarata Date: Sun, 5 Jul 2026 17:38:30 -0400 Subject: [PATCH] =?UTF-8?q?fix(ci):=20rampart-gate.js=20=E2=80=94=20custom?= =?UTF-8?q?=20rule=20metadata.rampart=20must=20win=20over=20raw=20semgrep?= =?UTF-8?q?=20severity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Custom rules under rules/rampart/*.yml are all authored with `severity: ERROR` at the semgrep level (deliberate convention so semgrep never filters them out), carrying the real business severity separately in `metadata.rampart` (e.g. "HIGH", "MEDIUM"). The old precedence in the semgrep branch checked `rampartMeta === 'CRITICAL' || rawSev === 'ERROR'` first, so the `||` let the raw semgrep severity win before metadata.rampart was ever consulted — every custom-rule finding (all severity:ERROR by convention) got force-promoted to CRITICAL regardless of its documented rampart severity. Since CRITICAL always blocks (even in warn/ENFORCE=warn mode), this could hard-fail the Rampart PR Gate on any PR tripping a HIGH/MEDIUM custom rule. Fix: metadata.rampart now wins whenever present; raw semgrep severity is only used as a fallback for third-party ruleset findings that carry no rampart metadata. Verified against a real semgrep-report.json artifact (CI run 28683658719): the no-localstorage-token finding (rampartMeta: HIGH) moved from critical to high, dropping critical 1->0 and flipping the gate result under warn mode. Same bug + fix as ummeco/praycalc#58. --- .github/scripts/rampart-gate.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/scripts/rampart-gate.js b/.github/scripts/rampart-gate.js index 7ddee73..2ca6f05 100644 --- a/.github/scripts/rampart-gate.js +++ b/.github/scripts/rampart-gate.js @@ -160,13 +160,19 @@ if (tool === 'pnpm') { if (tool === 'semgrep') { // semgrep JSON: { results: [{ check_id, path, extra: { severity, metadata: { rampart } } }] } + // Custom rampart/ rules are authored with `severity: ERROR` so semgrep never + // filters them out, and carry the real business severity in `metadata.rampart` + // instead. That metadata must win over the raw semgrep severity — otherwise + // every custom rule (all `severity: ERROR` by convention) gets promoted to + // CRITICAL regardless of its own documented rampart severity. const results = report.results || []; for (const r of results) { const rawSev = r.extra?.severity || r.severity || 'WARNING'; const rampartMeta = r.extra?.metadata?.rampart || ''; let sev; - if (rampartMeta === 'CRITICAL' || rawSev === 'ERROR') sev = 'CRITICAL'; - else if (rampartMeta === 'HIGH' || rawSev === 'WARNING') sev = 'HIGH'; + if (rampartMeta) sev = rampartMeta.toUpperCase(); + else if (rawSev === 'ERROR') sev = 'CRITICAL'; + else if (rawSev === 'WARNING') sev = 'HIGH'; else if (rawSev === 'INFO') sev = 'LOW'; else sev = 'MEDIUM'; const filePath = r.path || 'unknown';