Safeguard
Application Security

A vendor-neutral framework for evaluating SAST tools

OWASP's Benchmark suite has run 2,740 fixed Java test cases since 2016, yet most SAST comparisons still amount to a vendor's self-reported false-positive number.

Safeguard Research Team
Research
6 min read

Choosing a static application security testing (SAST) tool usually comes down to a vendor's slide deck claiming "industry-leading accuracy," a number nobody can reproduce. The OWASP Benchmark project has offered a public alternative since 2016: a fixed suite of 2,740 runnable Java test cases across 11 CWE categories, scored as true-positive rate minus false-positive rate, so any tool's output can be checked against the same ground truth. NIST ran a parallel effort for over a decade through its SAMATE program and the Static Analysis Tool Exposition (SATE), publishing multi-tool comparisons that found accuracy varying not just by vendor but by CWE category within the same tool — a scanner strong on SQL injection (CWE-89) might miss most instances of unsafe deserialization (CWE-502). Meanwhile SARIF 2.1.0, an OASIS standard since 2020, became the de facto interchange format that GitHub Code Scanning, GitLab, and Azure DevOps all consume, turning "does it integrate with CI" from a marketing claim into a checkable fact. This piece lays out four axes — coverage, false-positive rate, language support, and CI integration — that let a security team score any SAST tool, including Safeguard's own, without depending on a single vendor's ranking.

Why do SAST comparisons fall apart without a shared benchmark?

SAST comparisons fall apart because vendors tune detection rules against their own test sets, so a self-reported "under 15% false-positive rate" from one tool is not measured the same way as another tool's number. OWASP Benchmark fixes this by holding the test suite constant: the same 2,740 Java cases, split between genuinely exploitable vulnerabilities and intentionally similar-looking safe code, scored with one formula (TPR − FPR) per CWE category. NIST's SATE program ran a comparable exercise for C, C++, and Java tools between 2008 and roughly 2013, and its published reports explicitly warned that raw finding counts were meaningless without knowing which findings were true positives — a tool that reports zero findings scores as "accurate" on a metric that only counts false positives. Any team evaluating SAST tools should ask a vendor whether they've published Benchmark or SATE-style scores, not just a headline accuracy percentage.

How should you actually measure coverage instead of counting rule names?

Coverage should be measured against a fixed taxonomy — CWE and the OWASP Top 10 — not by the raw number of rules a vendor lists, because rule counts inflate easily by splitting one check into many near-duplicates. The practical test: pick the CWEs your application is actually exposed to (CWE-89 SQL injection, CWE-79 XSS, CWE-798 hardcoded credentials, CWE-502 deserialization are common starting points for the SANS/CWE Top 25) and confirm the tool has a documented rule mapped to each one, with a reproducible test case you can run yourself. A tool advertising "500+ checks" that has never been scored against a shared CWE-mapped suite tells you less than a tool advertising 80 checks with a published Benchmark score per category. Coverage claims should also specify whether detection is purely lexical/pattern-based or dataflow-based (tracing a source to a sink) — the two produce very different result sets on the same codebase, and only the second one tells you whether a dangerous call is reachable from untrusted input.

What's a fair way to evaluate false-positive rate?

A fair evaluation runs the tool against a fixed, labeled test set — your own recent findings history if you have one, or a public suite like OWASP Benchmark if you don't — and computes both the true-positive rate and false-positive rate per category, not an aggregate "accuracy" number that hides category-level weaknesses. NIST's SATE reports repeatedly found that tools with strong aggregate scores still had specific CWE categories where false positives outnumbered true positives 5-to-1 or worse, meaning a team relying on the aggregate number would still drown in noise for exactly the bug class they cared about. A second, cheaper check: pull 20-30 of your own historical findings, half true and half already-triaged false positives, and see how the tool scores them — this costs an afternoon and reflects your actual codebase's idioms far better than any published benchmark can.

Why does language support vary so much between SAST tools?

Language support varies because dataflow analysis quality depends on how predictable a language's call graph is, and dynamically typed or heavily metaprogrammed languages make that graph far harder to build accurately. Statically typed, compiled languages — Java, C#, Go — let a tool resolve most method calls at analysis time, producing high-fidelity taint paths. JavaScript/TypeScript, Python, Ruby, and PHP all support reflection, dynamic dispatch, or monkey-patching that can redirect execution in ways no static analyzer can fully enumerate; this is a documented, structural limitation, not a quality gap any vendor will close by adding more rules. When evaluating a tool, ask specifically whether a language is "GA" (production dataflow analysis) or "preview" (best-effort, more false negatives) for your stack, and treat a vendor that doesn't distinguish the two as a red flag.

What should "CI integration" actually mean when you evaluate a tool?

CI integration should mean the tool outputs SARIF 2.1.0, exposes a CLI or API that can gate a pull request on severity/CWE thresholds, and reports results fast enough to run on every commit rather than a nightly batch — anything less pushes the tool into a side channel developers ignore. SARIF matters concretely: GitHub Code Scanning, GitLab, and Azure DevOps all natively render SARIF output as inline PR annotations, so a tool that only emits a proprietary HTML report forces your team to build custom tooling just to get findings in front of a developer at review time. The second practical test is policy: can you configure "block merge on any new CWE-89 or CWE-502 finding above a severity threshold" declaratively, or does every new rule require a manual triage pass before it can be enforced? Tools that can't answer both of those questions concretely are not CI-integrated in any way that changes developer behavior.

How Safeguard Helps

Safeguard's own SAST engine is rolling out in phases — today it covers JavaScript/TypeScript, Python, and Java, with more languages following — and it's built to be evaluated against exactly this framework rather than around it. Every finding carries the full source-to-sink dataflow trace and a CWE/OWASP mapping, so coverage claims are checkable per category instead of asserted in aggregate. Findings emit through the same unified model as Safeguard's SCA and DAST results, sharing correlation keys across engines: when a DAST-confirmed runtime issue maps back to the SAST source-code sink that produced it, the two findings are linked, and that confirmed, reachable issue is prioritized above an isolated static-only guess — an honest differentiator most SAST-only tools can't offer, since they never see whether a flagged path actually executes in a running system. Results land in SARIF-compatible findings views and can gate CI directly — safeguard findings list --engine sast --severity high — so a team can hold Safeguard to the same coverage, accuracy, language-support, and integration checks this piece describes, on their own codebase, rather than taking a vendor's word for it.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.