P1 โ€” SAST+DAST Triage Tool


Problem

Raw scanner output is noisy. The same SQLi vulnerability at app.py:42 shows up in Semgrep and Bandit as two separate findings. Security teams waste hours deduplicating and filtering before they can triage what matters.

What It Does

  1. Normalize โ€” parse Semgrep JSON, Bandit JSON, and ZAP XML into one schema with rule_id, cwe, file, line, severity, snippet
  2. Deduplicate โ€” key on {cwe}:{file}:{line}. Same vulnerability, different scanner = one finding
  3. Score โ€” risk score 0โ€“10 based on severity + finding source (dynamic findings boosted) + CWE weight (SQLi/XSS/path traversal get extra)
  4. Filter FPs โ€” local LLM (hermes3:70b on Ollama) reviews each finding with code context โ†’ status: confirmed | needs_review | likely_fp
  5. Export โ€” SARIF 2.1.0 with suppressions block for likely_fp findings โ†’ direct upload to GitHub Code Scanning

Key Technical Decisions

CWE-based dedup over rule-name matching โ€” scanners use inconsistent names but share a CWE taxonomy. CWE-89:app.py:42 is the same issue regardless of which tool found it.

Local LLM only โ€” all code stays on-prem. The model sees: rule, CWE, severity, file path, 10-line snippet. Returns structured JSON verdict. No cloud calls.

SARIF suppressions โ€” likely_fp findings are preserved in the output but marked as dismissed. The GitHub dashboard stays clean; the data isn’t lost.

Results

On a test Django app (Semgrep + Bandit + ZAP): 400 raw findings โ†’ 180 deduplicated โ†’ 142 after FP filter. ~65% reduction in triage burden.

โ† back