CS336 · Program Analysis for Security & Privacy

Find taint bugs
before they ship.

A static taint analysis tool that tracks untrusted user input through your program — catching SQL injection, XSS, and command injection without running it.

How it works

Three-phase analysis

TaintTrace statically propagates taint labels through your program's AST, merging branches with sound over-approximation and reaching a fixed point over loops.

01 — SOURCE

Mark user input

Any value entering from input, get_param, request.args etc. is marked tainted.

02 — PROPAGATE

Track through code

Assignments, binary ops, if/else branches, and while loops — taint flows through all of them. Fixed-point iteration handles loops.

03 — SINK

Detect violations

If a tainted value reaches a dangerous operation like sql_exec or eval without being sanitized, a warning is emitted.

04 — SANITIZER

Respect clean paths

Calls to escape, parameterize, or your custom sanitizers remove the taint label — no false alarm.

Installation

Get running in under a minute

Choose your platform. OCaml required for .imp files; Python 3.8+ for native .py analysis.

# 1. Install OCaml and Dune (if you don't have them)
brew install opam                              # macOS
sudo apt install opam build-essential -y       # Ubuntu/Debian
opam init -y && eval $(opam env)
opam install dune -y

# 2. Clone and build
git clone https://github.com/yourusername/taint_analysis
cd taint_analysis
dune build

# 3. Run on any .imp file
dune exec src/main.exe -- tests/test1_sqli.imp

# 4. Or supply a custom policy
dune exec src/main.exe -- --policy policies/webapp.json tests/test1_sqli.imp
# Open WSL (Windows Subsystem for Linux)
wsl

# Install dependencies
sudo apt install opam build-essential -y
opam init -y && eval $(opam env) && opam install dune -y

# Build and run
cd ~/taint_analysis
dune build
dune exec src/main.exe -- tests/test1_sqli.imp
# No installation needed beyond Python 3.8+
python3 --version          # must be 3.8 or newer

# Run directly on any .py file
python3 python_frontend/taint_py.py tests/test_sqli.py

# With a custom policy file
python3 python_frontend/taint_py.py --policy policies/webapp.json tests/test_sqli.py
Demo

See taint analysis in action

Six representative programs — from raw injection to properly sanitized code. Click any card to explore.

test1_sqli.imp ⚠ WARNING
# tainted input flows to sql_exec
input user
query := user
sql_exec(query)
WARNING Tainted data at line 3
Source: input → user (line 1)
Sink: sql_exec (line 3)
Path: user → query → sql_exec
test2_sanitized.imp ✓ CLEAN
# sanitizer removes taint
input user
sanitize(user)
sql_exec(user) # now safe
No taint violations found. Program is clean.
test3_propagation.imp ⚠ WARNING
# taint tracked through chain
input x
a := x
b := a
html_output(b)
WARNING Tainted data at line 4
Path: x → a → b → html_output
test4_conditional.imp ⚠ WARNING
# branch merging (sound over-approx)
input user
if user then
  safe := 1
else
  safe := user # tainted branch
end
sql_exec(safe)
WARNING Branch merging catches taint
'safe' may be tainted (else branch)
test5_loop.imp ⚠ WARNING
# fixed-point catches taint in loops
input x
acc := 0
while x do
  acc := acc + x
end
sql_exec(acc)
WARNING Fixed-point iteration detects
taint accumulated in loop variable
test6_clean.imp ✓ CLEAN
# no taint — no false positives
x := 42
y := x + 1
sql_exec(y)
No taint violations found. Program is clean.
Test suite

All test cases explained

Complete reference for every bundled test — what it checks and what output to expect.

File Language What it checks Expected
test1_sqli.imp IMP User input flows directly to sql_exec with no sanitization 1 WARNING
test2_sanitized.imp IMP sanitize() is called before the sink — verifies false-positive suppression CLEAN
test3_propagation.imp IMP Taint tracked through a multi-hop assignment chain (x → a → b → sink) 1 WARNING
test4_conditional.imp IMP Branch merging: only one branch assigns a tainted value, but analysis is sound 1 WARNING
test5_loop.imp IMP Fixed-point iteration detects taint propagated through a while loop accumulator 1 WARNING
test6_clean.imp IMP Only constant data reaches the sink — validates zero false positives on clean code CLEAN
test_sqli.py Python Real Python: input() flows to cursor.execute() — classic SQLi 1 WARNING
test_sanitized.py Python Real Python: parameterize() cleans input before cursor.execute CLEAN
test_xss.py Python Flask route: request.args.get() flows to render_template_string() 1 WARNING
test_cmdinject.py Python OS command injection: user input reaches os.system() 1 WARNING
External Policy

Define your own sources, sinks, sanitizers

Pass any JSON policy file with --policy to override the built-in rules. Mix and match to fit your tech stack — three bundled policies included.

sources

Functions or constructs that introduce untrusted data into the program.

input, get_param, request.args.get …

sinks

Dangerous operations that must never receive tainted data without sanitization.

sql_exec, eval, os.system, innerHTML …

sanitizers

Functions that clean tainted data — calling them removes the taint label.

escape, parameterize, html_escape …
{
  "sources": [
    "input",
    "get_param",
    "post_param",
    "cookie"
  ],
  "sinks": [
    "sql_exec",
    "html_output",
    "eval"
  ],
  "sanitizers": [
    "my_custom_escape",
    "validate_input"
  ]
}

Bundled policies: policies/default.json · policies/webapp.json · policies/cmdinject.json

Real Language Support

Analyze real Python code

python_frontend/taint_py.py uses Python's built-in ast module to analyze real .py files — no OCaml needed. It detects SQLi, XSS, and command injection in Flask, Django, and plain Python.

Flask XSS example — test_xss.py ⚠ WARNING
from flask import Flask, request, render_template_string

@app.route("/search")
def search():
  query = request.args.get("q") # SOURCE
  template = f"<h1>{query}</h1>"
  return render_template_string(template) # SINK — XSS!
WARNING Tainted data flows to sink at line 10
Source: request.args.get at line 8
Sink: render_template_string at line 10
Path: request.args.get → template → render_template_string
✓ Assignment propagation
✓ if/else branch merging
✓ for/while fixed-point
✓ Function call sinks
✓ Method call sinks (cursor.execute)
✓ Flask / Django sources
✓ Handler auto-detection
✓ Custom policy JSON