A fresh Semgrep or CodeQL run against a real codebase can return hundreds of findings, most of them noise — and the noise is what kills scanner adoption, because developers learn to ignore a tool that cries wolf.
AI is well suited to the classification work in the middle: given a finding and the surrounding code, it can reason about whether the tainted path is actually reachable, explain why in terms a developer will accept, and cluster the pile so you triage patterns instead of individual lines. What it cannot do is decide, unaudited, that a finding is safe to close. This playbook keeps you in that loop.
An AI that labels a finding "false positive" is proposing a hypothesis, not closing a ticket. Every dismissal must be confirmed by a human tracing the actual data flow — a wrong dismissal is a shipped vulnerability with a paper trail that says you looked.
Export findings as structured data so you can feed them back with context:
semgrep --config auto --sarif services/ > /tmp/findings.sarif
For each finding, give the model the rule, the code, and the data-flow question. Ask for a reachability judgment with reasoning:
You are triaging a static-analysis finding in code I own. Decide whether
it is a TRUE positive (exploitable), a FALSE positive, or NEEDS REVIEW.
Base your decision on whether attacker-controlled input can actually
reach the sink. State the exact data-flow path if true positive, or the
specific reason it cannot be reached if false positive. If you cannot
determine reachability from the code shown, say NEEDS REVIEW and name
what additional code you'd need to see.
RULE: <rule id + message>
CODE (with surrounding context):
<paste the function + callers>
Expected output per finding: a verdict, a data-flow path or a reason-not-reachable, and — for anything it can't resolve — an explicit request for more context rather than a guess.
Cluster the pile before you drill in. Ask the model to group findings by root cause:
Here are 140 findings. Group them by underlying cause (same sanitizer
not recognized, same framework guard, same genuinely-vulnerable pattern).
For each cluster: the shared cause, count, and whether the cluster as a
whole is likely true or false positive.
Triaging 8 clusters is tractable where 140 findings is not, and a whole cluster can often be suppressed with one rule tweak or confirmed with one code fix.
Confirm true positives, then hand them to remediation. Route the survivors into the remediation-guidance and regression-test playbooks.
Tune the rules for confirmed false-positive clusters. Where a cluster is noise because the scanner doesn't understand a real sanitizer, encode that — a Semgrep pattern-not or a CodeQL sanitizer definition — so the noise never returns.
pattern-not rules make it ideal for the export → triage → suppress-cluster loop.Findings reports embed file paths, code, and sometimes secrets. Before prompting, redact real paths and any credential or customer identifier in the code context, and strip proprietary logic down to the security-relevant control flow. If the client bars external AI tools, run a local model for the classification prose — the scanners themselves (Semgrep, CodeQL, Trivy) run entirely offline and produce the SARIF you triage. Keep the audit trail of dismissals inside the client's own tracker, and get per-client approval before any finding leaves their boundary.