SAST: static application security testing explained
SAST (static application security testing) analyzes source code or compiled code without running the app, and flags patterns that look like security flaws, such as untrusted input reaching a SQL query. It scales across a codebase and points to the exact line. It misses flaws that depend on runtime behavior, configuration and business logic, and it produces false positives that need triage.
SAST, short for static application security testing, analyzes an application's source code or compiled code for security flaws without running it. A SAST tool reads the code, follows how data moves through it, and reports places that match a known flaw pattern, with the file and line.
What is SAST (static application security testing)?#
SAST means testing the code itself, not the running system. OWASP describes "source code analysis tools, also known as Static Application Security Testing (SAST) Tools" as tools that "can help analyze source code or compiled versions of code to help find security flaws."
Because the app never runs, SAST can start as soon as code exists. A developer can run it in an editor on an unfinished function. That is the main reason teams use it: a flaw found on the day it is written costs less to fix than one found after release.
SAST belongs to a family of testing methods. Methods that read code are "static". Methods that send requests to a running app are "dynamic", and a penetration test is the manual form of dynamic testing. Our types of penetration testing guide shows where each method sits.
How does SAST work, from source to sink?#
Most SAST rules follow one idea: find a source of untrusted input, find a sink that does something dangerous, and check whether data can travel from one to the other without cleaning.
A source is where outside data enters, such as a request parameter, a header or a file upload. A sink is a call that is dangerous with bad input, such as a SQL query, a shell command or an HTML output. A sanitizer is code that makes data safe for a given sink, such as a parameterized query.
The tool builds a model of the code, often a syntax tree plus a data flow graph, and walks it. If the path from source to sink has no sanitizer, the tool raises a finding. Simpler rules skip the data flow and match patterns, such as a call to a weak hash function or a hard-coded password in a string.
SAST tools also differ in what they read. Some read source code. Others read compiled code, such as bytecode. Compiled analysis helps when source is not available. OWASP notes that many tools struggle with code that cannot be compiled, and analysts often lack the right libraries or build instructions.
What does a SAST finding look like? A worked example with the fix#
Demonstration only
This is a demonstration. No real customer system was tested. The code belongs to Harbor Notes, a fictional note-taking app, and uses example.com only as a placeholder host.
The classic SAST finding is SQL injection, listed as CWE-89 by MITRE. Its description starts: "The product constructs all or part of an SQL command using externally-influenced input from an upstream component." The code below builds a query from a request parameter.
# notes/search.py (Harbor Notes, fictional)
def search_notes(request):
term = request.args.get("q") # source: user input
sql = "SELECT id, title FROM notes WHERE owner = %s AND title LIKE '%" + term + "%'"
rows = db.execute(sql, (request.user.id,)) # sink: SQL execution
return rows
A SAST tool reports a finding such as this one (format varies by tool):
rule: sql-injection (CWE-89)
file: notes/search.py line: 4
source: request.args.get("q") at line 2
sink: db.execute() at line 4
path: term -> sql (string concatenation) -> db.execute
sanitizer on path: none
The fix keeps user input out of the query text. Pass it as a bound parameter, so the database treats it as data.
def search_notes(request):
term = request.args.get("q", "")
sql = "SELECT id, title FROM notes WHERE owner = %s AND title LIKE %s"
rows = db.execute(sql, (request.user.id, "%" + term + "%"))
return rows
After the change, rerun the tool. The finding should close because the path no longer carries a string into the query text. Add a test that sends a quote character in q and expects a normal empty result. That test protects the fix from later edits.
What does SAST find, and what does it miss?#
SAST is good at flaws that have a visible code pattern. It is weak at flaws that depend on how the running system behaves. OWASP lists both sides, and its list is the best neutral summary we found.
| Strength or weakness | What OWASP says | Example |
|---|---|---|
| Scales | "Can be run on lots of software, and can be run repeatedly" | Every pull request |
| Known flaw classes | Identifies "certain well-known vulnerabilities" such as buffer overflows and SQL injection | CWE-89 above |
| Points to code | Reports "filename, location, line number, and even the affected code snippet" | Line 4 of search.py |
| Hard to automate | Authentication problems, access control issues, insecure use of cryptography | A missing ownership check |
| Limited coverage | "They can automatically identify only a relatively small percentage of application security flaws" | Logic flaws |
| Configuration | "Frequently unable to find configuration issues, since they are not represented in the code" | A public storage bucket |
| Proof | "Difficult to 'prove' that an identified security issue is an actual vulnerability" | Is the sink reachable? |
Access control is the clearest gap. A tool cannot know that user A should not read user B's note, because that rule lives in your product design. Testers find such flaws with two accounts, as in our IDOR testing guide, and with the methods in business logic vulnerabilities.
How do you triage SAST false positives?#
A false positive is a finding that does not describe a real vulnerability. OWASP lists "high numbers of false positives" as a SAST weakness, so triage is part of the job. Work through each finding with the same short checklist.
- Read the source and sink. Is the source really attacker-controlled? A constant or an internal ID is not.
- Read the path. Is there a sanitizer the tool did not recognize, such as a custom escape function or a framework feature that binds parameters?
- Check reachability. Is the code called at all? Dead code and test code rarely matter.
- Try to reproduce it. If the code is reachable, send a safe test input to a test environment and watch the result.
- Decide and record. Mark the finding as fix, accepted risk or false positive, with a reason and the date.
- Tune the rule. If one rule produces many false positives, add the custom sanitizer to the tool or narrow the rule to the paths it fits.
Keep a suppression file under version control, and require a reason on each entry. A bare "ignore" hides a real flaw the next time the code changes. Review suppressions when a major dependency or framework version changes.
Where does SAST run: IDE, pull request or CI?#
SAST can run at four points. Each trades speed for depth.
| Where | What runs | Trade-off |
|---|---|---|
| Editor (IDE) | Fast rules on the open file | Quick feedback, shallow analysis |
| Pre-commit | Secret and pattern checks on changed files | Very fast, narrow |
| Pull request | Rules on the changed code and the files it touches | Developer sees findings while the change is fresh |
| Scheduled CI | Full scan of the whole repository | Deepest, slowest, runs nightly or weekly |
A common setup runs fast checks on each pull request and a full scan on a schedule. Block merges only for high-confidence rules at first. If every finding blocks the build, developers learn to dodge the tool. Raise the bar as the false positive rate falls.
How is SAST different from DAST and a pentest?#
SAST reads code. DAST (dynamic application security testing) sends requests to a running app and watches the responses. A penetration test adds a person who chains findings, tests business rules and proves impact.
| Question | SAST | DAST | Penetration test |
|---|---|---|---|
| Needs running app | No | Yes | Yes |
| Needs source code | Yes (or compiled code) | No | Not for black box |
| Finds config problems | Rarely | Sometimes | Yes |
| Finds access control flaws | Rarely | Rarely | Yes, with two accounts |
| Proves impact | No | Partly | Yes, with evidence |
| Best moment | While writing code | In test or staging | Before launch and after major changes |
NIST SP 800-115, published in 2008 and still listed as final, covers technical testing and assessment techniques and their benefits and limitations. It is a useful base for planning, though it predates modern pipelines.
A pentest also tells you which SAST findings matter. If the tool flags 40 issues, a tester can confirm which ones an outside attacker can reach and which are dead paths. Read what is penetration testing for the process. For a neutral view of how scanners and pentests differ, see the PentestCompare scan versus pentest guide.
SAST is one input to your security program, not proof that the app is secure. Pair it with a test of the running system before a launch or a customer security review.
Need a pentest you can act on?
Tell us what you need tested and why. We reply to agree scope, and testing starts only after written authorization.
Questions
Is SAST the same as a code review?
No. A code review is a person reading changes. SAST is a program that applies rules to the whole codebase. The two find different things. A reviewer can judge intent and business rules. A tool can check every file on every commit and never gets tired.
Can SAST replace a penetration test?
No. SAST reads code and never sends a request to the running system. It cannot see deployed configuration, live access control or how services behave together. A pentest tests the running app. Teams usually run both, at different points.
How many SAST findings are false positives?
No vendor-neutral figure exists that we could source. OWASP lists high numbers of false positives as a weakness of SAST tools. The rate depends on the tool, the language, the rule set and how well you tune it. Measure it on your own code.
Do I need SAST if I use a memory-safe language?
Yes, for other flaw classes. Injection, hard-coded secrets and unsafe deserialization happen in any language. A memory-safe language removes buffer overflows but not a string-built SQL query.
Sources
- OWASP Community: Source Code Analysis Tools (checked 9 November 2026)
- MITRE CWE-89: Improper Neutralization of Special Elements used in an SQL Command (checked 9 November 2026)
- NIST SP 800-115: Technical Guide to Information Security Testing and Assessment (checked 9 November 2026)