untrusted input flowing into a dangerous sink

Injection & Taint Flaws (SAST)

SAST reads the source you wrote and follows untrusted data into dangerous sinks -- SQL, the shell, the filesystem -- plus hardcoded secrets and weak crypto. These are the defects a static scanner flags before the code ever runs.

Static analysis traces a tainted source to a sink; the fix is to break the flow.

01in the wild

In the wild

SQL Injection

Untrusted input concatenated into a query is the classic source-to-sink flow.

example.py
# SMELL: untrusted input concatenated into SQL (the classic sink)
q = "SELECT * FROM users WHERE name = '" + name + "'"
db.execute(q)               # name = "' OR '1'='1" dumps the table

# RIGHT: parameterize -- data never becomes code
db.execute("SELECT * FROM users WHERE name = ?", (name,))
SAST traces the tainted name from an untrusted source into the query sink. Parameterized queries keep data and code separate.
// observed
concat: ' OR '1'='1 returns every row
param:  the input is data, never SQL
example.java
// SMELL: a string-built query -> injectable
String q = "SELECT * FROM acct WHERE id = " + req.getParameter("id");
stmt.executeQuery(q);

// RIGHT: PreparedStatement binds the value safely
PreparedStatement ps = conn.prepareStatement(
    "SELECT * FROM acct WHERE id = ?");
ps.setInt(1, Integer.parseInt(req.getParameter("id")));
The request parameter is tainted source; the query is the sink. Binding parameters (and parsing to int) breaks the injection.
// observed
concat:   1 OR 1=1 leaks all accounts
prepared: only a valid id is accepted

Command Injection & Path Traversal

Input handed to a shell or a file path escapes into commands and directories you never intended.

example.js
// SMELL: user input handed straight to a shell
exec(`convert ${userFile} out.png`);   // userFile = "x; rm -rf /"

// RIGHT: no shell; pass arguments as an array
execFile("convert", [userFile, "out.png"]);
String-interpolating input into a shell command is a sink. execFile bypasses the shell, so metacharacters are inert.
// observed
exec:     '; rm -rf /' runs as a command
execFile: the filename is just an argument
example.py
# SMELL: user-controlled path escapes the intended directory
path = os.path.join(UPLOADS, request.args["name"])   # name = "../../etc/passwd"
return open(path).read()

# RIGHT: resolve and confirm it stays inside UPLOADS
full = os.path.realpath(os.path.join(UPLOADS, name))
if not full.startswith(UPLOADS):
    abort(403)
return open(full).read()
../ sequences in tainted input walk outside the upload dir. Resolve the real path and verify containment before opening.
// observed
naive:   reads /etc/passwd
guarded: 403 on any escape attempt

Hardcoded Secrets

A credential committed in source leaks through the repo and can't be rotated without a deploy.

example.py
# SMELL: a secret committed in source -- SAST and secret scanners flag this
API_KEY = "sk_live_51H8xQ2eZvKf9pQ"      # now in git history forever

# RIGHT: read from the environment / a secret manager
API_KEY = os.environ["API_KEY"]
Hardcoded credentials are a static-analysis red flag: they leak through the repo's history and can't be rotated without a code change.
// observed
hardcoded: leaked the moment the repo is shared
env var:   rotate without touching code
02weakness catalog

Mapped weaknesses (CWE)

On its own, this defect is catalogued by MITRE as one or more of these weaknesses. The exploitable vulnerability usually appears only when it chains or combines with another.