Commit 35f9f3c
authored
fix(code_scanning_alerts): break CSA001 self-referential echo loop (#328)
## Why
\`CSA001-CSA004\` (\`lib/rules/code_scanning_alerts.ex\`) are lenses
over GitHub's code-scanning alerts — they query
\`/repos/<owner>/<repo>/code-scanning/alerts\` and echo each as a
Hypatia finding so a single run surfaces everything GitHub's Security
tab shows.
The findings then get uploaded back to GitHub as SARIF, creating new
alerts under \`tool: Hypatia\`, \`rule_id:
hypatia/code_scanning_alerts/CSA00X\`. Without a filter, the **next**
Hypatia scan sees those alerts via CSA001 and echoes them again,
generating fresh CSA findings about previous CSA findings.
## The observed failure mode (boj-server)
boj-server accumulated 30+ self-referential alerts on
\`cartridges/*/ffi/cartridge_shim.zig\` (alert numbers 357-386, all
created at exactly \`2026-05-24T20:07:32Z\` — single scan emitted 30
echoes in one go). The PR check Hypatia failed and blocked PR #149
(\"Claude/repo tidy rsr taxonomy\") even though the PR didn't touch the
code-scanning surface at all.
The message field of each looped alert showed the self-echo chain
explicitly — the \`description\` slot was filled with the previous
round's rendered \`build_alert_reason/5\` output, so each round nested
one level deeper:
> Code scanning (Hypatia): hypatia/code_scanning_alerts/CSA001 --
Hypatia code_scanning_alerts: CSA001 -- 1 day(s) old
## The fix
Adds \`self_referential_alert?/1\` — a predicate that returns true when:
- \`tool.name == \"Hypatia\"\`, AND
- \`rule.id\` starts with \`hypatia/code_scanning_alerts/\`
\`fetch_alerts/2\` applies it as an \`Enum.reject\` filter immediately
after JSON decode, so all four CSA rules see a CSA-free alert list and
cannot generate fresh CSA findings about previous CSA findings.
### Why narrow
The predicate **only** suppresses this module's own emissions. Real
findings from other Hypatia rule modules (\`code_safety\`,
\`supply_chain\`, \`workflow_hardening\`, etc.) continue to surface via
CSA001 even when they happen to carry \`tool: Hypatia\`, because they do
**not** match the \`rule.id\` prefix. CodeQL and third-party SARIF
findings are unaffected.
## Tests
Six regression cases in \`test/code_scanning_alerts_test.exs\`:
- CSA001 self-echo (the primary failure mode)
- CSA002 / CSA003 / CSA004 echoes (same loop mechanism)
- CodeQL alert passes through (real findings still surface)
- Third-party SARIF (Snyk) alert passes through
- Hypatia non-CSA finding passes through (other rule modules unaffected)
- Missing \`tool\` / \`rule\` keys handled gracefully
Local \`mix test\` couldn't complete in this environment (Phoenix dep is
incompatible with Elixir 1.14 here); CI will run the suite.
## Cleanup follow-up
Once this lands, the 30+ existing self-referential alerts on boj-server
can be dismissed in one batch:
\`\`\`bash
gh api repos/hyperpolymath/boj-server/code-scanning/alerts --paginate \\
--jq '.[] | select(.rule.id == \"hypatia/code_scanning_alerts/CSA001\"
and .state == \"open\") | .number' \\
| xargs -I{} gh api -X PATCH \\
repos/hyperpolymath/boj-server/code-scanning/alerts/{} \\
-f state=dismissed -f dismissed_reason=\"false positive\" \\
-f dismissed_comment=\"Self-referential CSA001 echo loop; see
hypatia#$THIS_PR\"
\`\`\`
## Refs
- boj-server #149 (the visible failure)
- Earlier memory: \`project_hypatia_scorecard_loop_closer.md\` (related
alert-lifecycle pattern; this is a sibling fix in the same class)
## Architectural note (separate consideration, not in this PR)
The deeper question is whether Hypatia's SARIF-upload step should
exclude **lens** rules (CSA001-004, plus Dependabot lens equivalents)
from upload entirely — uploading \"this is what GitHub already shows\"
back to GitHub Code Scanning is by definition redundant. That's a larger
architectural change; this PR is the defensive belt-and-braces filter
that stops the bleeding immediately.
## Test plan
- [ ] CI green (especially the new \`describe
\"self_referential_alert?/1\"\` block)
- [ ] After merge, run the batch dismissal on boj-server and confirm PR
#149's Hypatia check goes green
- [ ] Confirm no regression in real CSA001 surfacing (CodeQL findings on
a fresh repo should still appear)1 parent 75c3019 commit 35f9f3c
2 files changed
Lines changed: 103 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
318 | 318 | | |
319 | 319 | | |
320 | 320 | | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
321 | 346 | | |
322 | 347 | | |
323 | 348 | | |
| |||
343 | 368 | | |
344 | 369 | | |
345 | 370 | | |
346 | | - | |
347 | | - | |
348 | | - | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
349 | 379 | | |
350 | 380 | | |
351 | 381 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
68 | 138 | | |
0 commit comments