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.
In the wild
SQL Injection
Untrusted input concatenated into a query is the classic source-to-sink flow.
# 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,))concat: ' OR '1'='1 returns every row param: the input is data, never SQL
// 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")));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.
// 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"]);exec: '; rm -rf /' runs as a command execFile: the filename is just an argument
# 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()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.
# 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: leaked the moment the repo is shared env var: rotate without touching code
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.
- CWE-74 — Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')
- CWE-89 — Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
- CWE-78 — Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')
- CWE-79 — Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')