mode: taint
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:
| Key | Items |
|---|---|
pattern-sources | Code that produces untrusted data, such as reading a request parameter. |
pattern-sinks | Code that must not receive untrusted data, such as running a SQL query. |
pattern-sanitizers | Code that makes data safe, such as escaping it. Optional. |
pattern-propagators | Operations 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
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: ERRORimport os, shlexname = input()# ruleid: user-input-to-systemos.system("ls " + name)# ok: user-input-to-systemos.system("ls " + shlex.quote(name))$ opengrep test .
1/1: ✓ All tests passed
No tests for fixes found.The path of the data
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: ERRORdef search(): name = request.args.get("name") query = "SELECT * FROM users WHERE name = '" + name + "'" cursor.execute(query)$ 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)