reference Rule syntax

mode: taint

See also
pattern-sources, pattern-sinks, pattern-sanitizers, pattern-propagators, label, taint_intrafile, taint_interfile, --taint-intrafile, --enable-interfile, --dataflow-traces, --guarded-taint-signatures, control, mode, taint_assume_safe_functions, taint_focus_on
mode: taint
pattern-sources: [...]
pattern-sanitizers: [...]    # optional
pattern-propagators: [...]   # optional
pattern-sinks: [...]

A taint rule reports code where data from a source reaches a sink. Instead of a single pattern, it has four lists:

KeyItems
pattern-sourcesCode that produces untrusted data, such as reading a request parameter.
pattern-sinksCode that must not receive untrusted data, such as running a SQL query.
pattern-sanitizersCode that makes data safe, such as escaping it. Optional.
pattern-propagatorsOperations that pass taint from one value to another where the analysis does not detect it. Optional.

Each item is a mapping with a pattern, written with the keys of a search rule (pattern, patterns, pattern-either, pattern-regex, …), and optional keys that refine it: label and requires to distinguish kinds of taint, exact, by-side-effect and control.

How taint propagates

The data that a source matches is tainted. Taint propagates through assignments, through the expressions built from a tainted value, and from the arguments of a call to its result. Fields are tracked separately: a tainted user.name does not taint user.id. Data that passes through a sanitizer is no longer tainted. A finding is reported at each sink that tainted data reaches, and --dataflow-traces shows the path it took.

The rule options whose names start with taint_ change these rules, for example taint_assume_safe_functions, which stops taint at function calls.

Scope of the analysis

By default, the analysis stays within one function: taint passed to another function is not followed into it. The option taint_intrafile, or the flag --taint-intrafile, follows calls to functions in the same file. The option taint_interfile, together with the flag --enable-interfile, follows calls across the files of a project.

Examples

User input reaching a shell

user-input-to-system.yaml
rules:  - id: user-input-to-system    mode: taint    pattern-sources:      - pattern: input(...)    pattern-sanitizers:      - pattern: shlex.quote(...)    pattern-sinks:      - pattern: os.system(...)    message: user input reaches os.system    languages: [python]    severity: ERROR
user-input-to-system.py
import os, shlexname = input()# ruleid: user-input-to-systemos.system("ls " + name)# ok: user-input-to-systemos.system("ls " + shlex.quote(name))
Command and result
$ opengrep test .
1/1: ✓ All tests passed
No tests for fixes found.

The path of the data

sql-injection.yaml
rules:  - id: sql-injection    mode: taint    pattern-sources:      - pattern: request.args.get(...)    pattern-sinks:      - pattern: cursor.execute(...)    message: user input reaches a SQL query    languages: [python]    severity: ERROR
app.py
def search():    name = request.args.get("name")    query = "SELECT * FROM users WHERE name = '" + name + "'"    cursor.execute(query)
Command and result
$ opengrep scan -q --config sql-injection.yaml --dataflow-traces app.py
app.py

  error  sql-injection
  user input reaches a SQL query

     4: cursor.execute(query)
        │
        ├─ source           2: name = request.args.get("name")
        ├─ through          2: name = request.args.get("name")
        ├─ through          3: query = "SELECT * FROM users WHERE name = '" + name + "'"
        └─ sink             4: cursor.execute(query)