Safeguard
Application Security

How Does SAST Work? Stages of SAST Scanning

A stage-by-stage breakdown of how SAST scanning actually works — parsing, taint analysis, false positives — with real CVEs and benchmark data.

Priya Mehta
DevSecOps Engineer
7 min read

Static Application Security Testing (SAST) scans source code, bytecode, or binaries without executing the program, looking for patterns that indicate exploitable flaws — SQL injection, hardcoded secrets, path traversal, insecure deserialization. A typical SAST engine parses a codebase into an abstract syntax tree, builds control-flow and data-flow graphs on top of it, then walks those graphs looking for tainted data reaching a dangerous "sink" like a database query or a shell command. GitHub's 2023 Octoverse report found that over 90% of organizations with more than 500 developers run some form of static analysis in CI. But "runs a scan" and "understands how SAST actually works" are different things — and that gap is why teams end up drowning in 3,000-finding PDF reports instead of shipping fixes. This post walks through the concrete stages a SAST tool executes, from lexing to rule matching, and where the technique breaks down without additional context.

What Is SAST and Where Does It Run in the Development Lifecycle?

SAST analyzes code at rest — before a single line executes — which is why it's classified as a "white-box" testing method, in contrast to DAST's black-box approach against a running application. Tools like Semgrep, CodeQL, SonarQube, and Checkmarx read source files (or in some cases compiled bytecode, as with Java .class files or .NET IL) and evaluate them against a rule set mapped to known weakness classes, most commonly the MITRE CWE taxonomy — CWE-89 for SQL injection, CWE-798 for hardcoded credentials, CWE-22 for path traversal. Because it doesn't need a running environment, SAST is the technique most commonly wired directly into pull request checks and pre-commit hooks; a 2023 GitLab DevSecOps survey found 55% of security scanning now happens in the CI pipeline rather than as a pre-release gate. That's also why SAST runtimes matter operationally: scanning a 500,000-line monorepo with a naive rule engine can take 20-40 minutes, which is exactly the delay that gets a scanner disabled by an impatient engineering team six weeks after rollout.

What Are the Actual Stages of a SAST Scan?

A SAST scan runs through five distinct stages: lexing/parsing, intermediate representation, control- and data-flow graph construction, taint propagation, and rule matching against a vulnerability signature database. First, the lexer tokenizes source text and the parser builds an Abstract Syntax Tree (AST) — a structured representation of every function, variable declaration, and expression, independent of formatting or whitespace. Second, the tool converts that AST into an intermediate representation (IR), a normalized form that lets one engine analyze multiple languages — CodeQL, for example, compiles code into a relational database queryable with a language called QL. Third, the engine builds a Control Flow Graph (CFG) mapping every possible execution path through a function, and a Data Flow Graph (DFG) tracking how variable values move between them. Fourth comes taint analysis, tracing untrusted input from a "source" (like an HTTP request parameter) to a "sink" (like cursor.execute()). Fifth, the engine matches discovered flows against thousands of signatures — Semgrep's public registry alone ships over 5,000 rules across 30+ languages as of its 2024 rule pack — and emits findings with a file, line number, and CWE classification.

How Does Taint Analysis Actually Catch a Vulnerability Like SQL Injection?

Taint analysis catches SQL injection by tracking whether attacker-controlled data flows, unsanitized, into a database call. Concretely: if a Flask route reads request.args.get('id') and that value is passed through two intermediate functions before landing in f"SELECT * FROM users WHERE id={user_id}", the taint engine marks the variable "tainted" at the source, follows it across each function boundary and reassignment via the data-flow graph, and flags the sink because no sanitizing function (parameterization, escaping, an allowlist check) sits between them. This is exactly the mechanism behind Equifax's 2017 breach, where CVE-2017-5638 in Apache Struts allowed a crafted Content-Type header to reach an OGNL expression evaluator — a taint path a properly configured SAST rule should have flagged years before the breach, since the Struts vulnerability class (CWE-94, code injection) had known signatures. Interprocedural taint tracking — following data across dozens of function calls and multiple files — is computationally expensive, which is why many SAST tools cap analysis depth and silently miss vulnerabilities that require tracing taint more than 5-10 hops from source to sink.

Why Do SAST Scans Generate So Many False Positives?

SAST generates high false-positive rates because it reasons about code structure without knowing runtime context — whether a "vulnerable" function is ever actually reachable from an entry point, or whether an upstream framework already sanitizes the input. A 2020 NIST study (SATE V) comparing commercial and open-source SAST tools against a benchmark of real CVEs found false-positive rates ranging from 30% to over 70% depending on the tool and language. In practice, this shows up as findings like a flagged eval() call inside a dead code branch behind a feature flag that's been off since 2022, or a "hardcoded secret" that's actually a placeholder string in a unit test fixture. Every one of those findings still costs a developer 5-10 minutes to triage, and at enterprise scale — a codebase generating 2,000+ raw findings per scan is common in monoliths over 1 million lines — that triage tax is the single biggest reason security teams report alert fatigue as their top complaint, ahead of tool cost, in Ponemon Institute's 2023 application security survey.

When in the SDLC Should SAST Actually Run, and What Should Trigger a Block?

SAST should run at commit time on the changed diff, at pull request time against the merged result, and on a full-codebase schedule at least weekly to catch drift from newly disclosed CWE patterns or updated rule packs. Diff-aware scanning (checking only the ~50-500 lines in a typical PR rather than the entire repo) is what makes sub-2-minute PR checks possible; full-repo scans, which catch cross-file taint paths a diff scan would miss, are better suited to nightly or weekly cadences given their 20+ minute runtimes on large codebases. As for what should actually block a merge: gating on every finding trains developers to click "override" within the first month — the fix is gating only on high-confidence, high-severity findings (critical/high CWE classes with a complete, confirmed taint path), while routing medium and low findings to a backlog with SLA tracking rather than a hard stop.

How Safeguard Helps

Safeguard runs SAST alongside reachability analysis so a finding isn't just "this function has a taint path" — it's confirmed against your actual call graph and, for open-source dependencies, cross-checked against your SBOM to show whether the vulnerable code path is even loaded at runtime, cutting through the 30-70% false-positive range that plagues traditional scanners. Griffin, Safeguard's AI reasoning layer, triages each finding by simulating exploitability and business context before it ever reaches an engineer's queue, so PR gates block on confirmed risk rather than raw rule matches. Safeguard generates SBOMs automatically on every build and can ingest existing SBOMs (CycloneDX or SPDX) from your pipeline to correlate SAST findings with known-vulnerable components in one view. For confirmed, high-confidence findings, Safeguard opens an auto-fix pull request with the patch already applied — sanitization added, parameterized query substituted, secret rotated out of source — so remediation ships in the same PR cycle the finding was raised in, not three sprints later in a backlog nobody triages.

Never miss an update

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