Skip to content
back to the map
testing technique
stage: build / PR
3 min read

SAST

An X-ray of the code without running anything: it finds vulnerable patterns before the first deploy.

SemgrepCodeQLSonarQubeCheckmarx

What it is

(Static Application Security Testing) analyzes source code, bytecode or binaries without running the program. The scanner looks for dangerous constructs (SQL concatenation, , weak cryptography, ) and points to the file and line, in the format developers already consume.

The acronym hides three very different techniques: pattern matching (textual rules), data flow analysis (the famous ) and semantic analysis. Without understanding all three, the scanner output looks like magic, and false positives look like bugs.

From text to AST

Before any analysis, the code goes through a parser and becomes an AST (): each language construct becomes a typed node, and the scanner navigates this tree instead of reading loose text. That is what lets it ask things like 'is there a concatenation node inside a query call?'.

An innocent line of JavaScript becomes a tree like this:

sql.js → ASTtext
const sql = "SELECT * FROM users WHERE id = " + req.query.id

VariableDeclaration
  id:   Identifier "sql"
  init: BinaryExpression "+"
          left:  StringLiteral "SELECT * FROM users WHERE id = "
          right: MemberExpression
                   req.query.id

Source → sink: the heart of the analysis

The AST alone says a concatenation exists, not whether it is dangerous. The security question is a different one: does this data come from a trusted origin? Where does it end up?

Taint analysis answers by following the data: untrusted sources (user input, headers, query strings) are marked as 'tainted'. The engine propagates this mark through assignments, concatenations and function calls, and raises the alert when the mark reaches a sink: a point where the data becomes a command, query, HTML or file path.

In the example below, the flow is complete: it is born at req.query.id (source), crosses a concatenation (propagation) and ends inside db.query (sink):

server.js — vulnerablejavascript
app.get("/users", (req, res) => {
  const id = req.query.id;                                // SOURCE
  const sql = "SELECT * FROM users WHERE id = " + id;     // propagation
  db.query(sql, (err, rows) => res.json(rows));           // SINK
});

What the scanner reports

The result is not 'a concatenation exists', it is the whole path, with the and a suggested fix. Something like:

scanner outputtext
server.js:3
  SQL Injection (CWE-89) · high
  taint:  req.query.id -> id -> sql -> db.query()
  fix:    use a parameterized query (placeholder $1)

The fix the engine accepts

Parameterizing the query breaks the taint: the user's value stops being code and becomes data. In taint analysis terms, the is a sanitizer (or barrier): the engine knows that specific path is clean and stops alerting on it.

server.js — fixedjavascript
app.get("/users", (req, res) => {
  const sql = "SELECT * FROM users WHERE id = $1";        // fixed query
  db.query(sql, [req.query.id], (err, rows) => res.json(rows));
});

Where it fits in the pipeline

As early as possible: IDE plugins and pre-commit give feedback in seconds, and the full analysis runs on the PR and in the build stage. Broken critical rules fail the build () and block the merge.

Strengths

  • 100% code coverage, including rarely executed paths
  • finds the vulnerability early, when fixing it is cheap
  • needs no running environment or credentials

Limitations

  • false positives: when the engine cannot prove the path is safe (a custom sanitizer, for example), it alerts as a precaution, and triage is human work
  • path explosion: analyzing every execution combination is expensive; engines use heuristics and can miss rare flows
  • it is blind to runtime configuration, infrastructure and real behavior, which is DAST territory
SASTStatic analysis: examines the code without running it. It finds issues through flow analysis, not guesswork.see in glossary →Insecure deserializationRebuilding objects from untrusted bytes with a format that can instantiate arbitrary types (Java serialization, pickle). The bytes can trigger code execution.see in glossary →XSScross-site scriptingInjection into a web page: untrusted input is rendered as HTML or script in another user's browser. Escape on output by context and add a Content-Security-Policy.see in glossary →Taint trackingtaint analysisFollowing the 'dirty data' mark from the source to the sink, propagating through assignments and calls. It is the heart of modern SAST.see in glossary →ASTabstract syntax treeThe syntax tree the parser produces: each code construct becomes a typed node, and tools analyze the tree instead of loose text.see in glossary →CWEcommon weakness enumerationThe identifier of the TYPE of weakness, the class of error in the code. CWE-89 is SQL injection, CWE-79 is XSS. The distinction that confuses everyone: CWE is the category, CVE is a concrete occurrence of it. A CVE for SQL injection in your framework will reference CWE-89.see in glossary →Prepared statementparameterized queryA query whose structure is fixed before any value is supplied, so the database cannot confuse data with instructions.see in glossary →Quality gateA stage's explicit gate: 'fail the build if there is a new critical finding'. Without a clear criterion, a gate becomes a suggestion.see in glossary →

connections on the map

updated on 14/10/2026 · sources to go deeper · report an error

in other domains

Study before

Unlocks

test what you learned

Loading questions…