Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.
SAST false positives are findings from a static application security testing tool that flag code as vulnerable when it isn't. They happen because most SAST tools match risky-looking patterns without understanding the code around them. You reduce them by teaching the tool your code, checking reachability before a person triages, and recording every decision.
The tool misses what a reviewer sees in seconds: a custom sanitizer, a framework protection, a function nobody calls, an endpoint that only runs behind the VPN. Below are the seven causes behind most of that noise, how to measure your rate honestly, and eight fixes in the order we'd apply them.
A SAST false positive is a finding that reports a vulnerability that doesn't exist. The flagged code can't be exploited the way the finding describes, usually because the input is already validated, the data never reaches the dangerous function, or the "dangerous" function isn't dangerous in that context. For the general term across all security tools, see the false positive glossary entry.
Teams often lump three different outcomes together. Only one of them is a false positive.
| Outcome | What it means | What to do |
|---|---|---|
| False positive | The vulnerability doesn't exist. | Dismiss, record why, and fix the rule or config that produced it. |
| True but not reachable | The flaw is real, but untrusted input can't reach it today (dead code, test fixture, disabled feature). | Deprioritize, keep it on record, re-check when the code changes. |
| True but low priority | The flaw is real and reachable, but the impact is small. | Rank it below real risk and fix it on a normal timeline. |
Calling all three "false positives" hides real flaws and makes your rate meaningless. A false positive is a wrong answer. An unreachable finding is a right answer you can schedule.
False positives cost more than triage time. They teach developers that security findings are usually wrong.
Across five Nullify production tenants, 57% of 14,847 SAST findings triaged over 90 days were ultimately classified as false positives. In an earlier limited deployment to ten repositories, triage turned up 547 false positives against 105 true positives. Five of every six findings in that deployment were noise.
At those ratios, volume hides the real findings. Someone has to read every finding to find the few that matter, and developers who've been burned before stop reading closely. The cost lands twice: in security team hours, and in the real vulnerability nobody acted on.
Static analysis reads code without running it. That forces the tool to guess how data flows, which functions are called and what the code is for. Most false positives come from one of these guesses going wrong.
Many rules flag syntax that looks dangerous: string concatenation near a SQL call, a variable passed to exec, a URL built from a parameter. The rule fires whether or not the value is attacker-controlled. A query built from a hardcoded table name and an integer ID matches the same pattern as a real injection.
Most codebases have their own validation helpers. If the tool doesn't recognize them, every path through them looks unsanitized.
```python from app.security import safe_identifier # allow-list of known column names
def sort_orders(request): column = safe_identifier(request.args["sort"]) # raises on anything unexpected return db.execute(f"SELECT * FROM orders ORDER BY {column}") ```
A pattern-based rule sees request input in an f-string passed to execute and reports SQL injection. A reviewer sees an allow-list and dismisses it. Until the tool learns that safe_identifier is a sanitizer, it reports this on every scan, in every file that uses it.
Frameworks handle a lot of security implicitly: ORMs parameterize queries, template engines escape output, middleware enforces authentication, decorators check permissions. A tool that doesn't model the framework reports the raw operation and misses the protection.
To avoid missing anything, static analysis assumes every branch can run and every call through an interface might reach any implementation. That's the safe choice, and it produces paths that can't happen at runtime. Go's own govulncheck documentation notes that it analyzes function pointer and interface calls conservatively, "which may result in false positives." Any static analyzer that tries not to miss bugs makes the same trade.
Test fixtures contain hardcoded credentials and deliberately unsafe inputs. Generated clients and vendored libraries contain patterns you'll never edit. Scanning them with production rules produces findings nobody can or should fix.
Code that's never called, or that sits behind a feature flag that's off, can contain real flaws that aren't exploitable today. These are "true but not reachable," but a tool that can't tell the difference reports them at full severity.
The code can't tell the tool whether an endpoint is internet-facing, admin-only or behind a VPN. Without that context, an internal maintenance script and a public checkout API get the same severity for the same pattern.
You can't reduce a rate you don't measure. Track triage outcomes for a fixed period, then calculate:
False positive rate (of findings triaged) = findings dismissed as false positive ÷ all findings triaged
Four rules keep the number honest:
To compare tools before buying, run them against a test suite with known answers. The OWASP Benchmark is an open-source Java and Python application built for this: its vulnerabilities are meant to be actually exploitable, and its scorecards show each tool's true positive and false positive rates side by side.
Work through these roughly in order. The first four are configuration you can do with any tool.
Exclude test directories, fixtures, generated code and vendored dependencies from production rules. Scan dependencies with SCA instead. This is often the quickest cut in noise, and it needs no rule changes.
Register your validation helpers as sanitizers, and make sure framework support is turned on for the frameworks you use. Every recognized sanitizer removes a whole class of repeat findings across the codebase.
Rank rules by false positive rate from your triage data. Fix the worst ones with tighter conditions, move low-value rules to an "audit" profile, and retire rules your team dismisses every time. Don't turn off whole vulnerability classes to make the count drop.
When someone dismisses a finding, record the reason, and make sure the dismissal survives rescans, refactors and file moves. Re-triaging the same false positive every week is the fastest way to lose a developer's attention.
Trace whether untrusted input (HTTP requests, API calls, file uploads) can actually reach the flagged code, and whether the code is called at all. Findings in dead code or behind disabled flags drop to the bottom of the queue with the reason attached.
Ask whether the affected service is deployed, whether it's exposed to the internet, and what controls sit in front of it. A finding that's reachable in code but runs only in an internal batch job ranks below the same finding in a public API.
Findings on a pull request are reviewed by the person who just wrote the code, while it's fresh. They're faster to confirm or dismiss, and real ones never reach the backlog. When you first roll a tool out on an existing codebase, baseline the findings already there and work them as a separate backlog, so new pull requests only show what the change introduced.
Every dismissal is a signal. Review the dismissal causes monthly and turn the top ones into config changes: a new sanitizer, a tighter rule, an excluded path. The goal is a rate that keeps falling, not a one-time cleanup.
Every fix above has a cheap version that lowers the count without lowering the noise. Each of these raises your false negatives, the real flaws a tool fails to report.
Nullify's code security scanning is AI-native static analysis that reasons about how your application works instead of matching signatures. It traces untrusted input top-down from HTTP requests, API calls and file uploads, and bottom-up from dangerous sinks back to their sources, with framework-aware analysis for Spring, Django, Flask, FastAPI, Rails, Express, Next.js, Laravel and others across 16 languages.
Before a person sees a finding, it's validated on reachability, exploitability and business context. Findings in unreachable code, test fixtures and benign configurations are filtered out, and findings are re-triaged when the code changes. Your team's overrides persist through that re-triage. When reachability is genuinely unclear, the system records "uncertain." It never upgrades a guess to "not reachable."
Nullify scans every pull request and every default-branch commit. When a finding is real, it opens a minimal, build-validated fix for your team to review. It never auto-merges.
A false positive is a wrong answer. Fix the rule that gave it, and it stops coming back.
There's no universal benchmark, because rates depend on the language, the rules and how you count. For reference, 57% of 14,847 SAST findings in Nullify's production data were ultimately false positives. The useful target is a rate that falls quarter over quarter, measured per rule.
Static analysis reads code without running it, so it has to guess how data flows and which code runs. To avoid missing real flaws, most tools guess conservatively, and they can't see your custom sanitizers, framework protections or deployment context. Each wrong guess becomes a finding.
No. If the flaw is real, it's a true finding that isn't reachable today. Rank it low and keep it on record, because a refactor or a new caller can make it reachable. Marking it false loses the history.
Yes, when it adds context the rules lack: whether input is attacker-controlled, whether a helper is a sanitizer, whether the code runs in production. AI triage should show its evidence for each decision, so a reviewer can audit it instead of redoing it.