Flask SQL Injection via Tainted String

CRITICAL

Finds user input reaching raw SQL queries in Flask apps where parameterized queries should be used instead.

Rule Information

Language
Python
Category
Flask
Author
Shivasurya
Shivasurya
Last Updated
2026-03-22
Tags
pythonflasksql-injectiondatabaseparameterized-queriescursor-executesqlitemysqlpostgresqlpymysqlpsycopg2cross-fileinter-proceduraltaint-analysisCWE-89OWASP-A03
CWE References

Interactive Playground

Experiment with the vulnerable code and security rule below. Edit the code to see how the rule detects different vulnerability patterns.

pathfinder scan --ruleset python/PYTHON-FLASK-SEC-003 --project .
1
2
3
4
5
6
7
8
9
10
11
rule.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Cross-file analysis: 2 files

About This Rule

Understanding the vulnerability and how it is detected

This rule catches a specific pattern: user input from Flask request parameters (request.args, request.form, request.get_json) gets concatenated or f-stringed into a SQL query, then passed to cursor.execute(). The fix is straightforward -- use parameterized queries instead of string building.

What makes this rule different from a simple grep for "execute" is that it actually traces data flow. If the user input arrives in app.py, gets assigned to a variable, passed through a function call into db.py, and ends up in cursor.execute() three hops later -- Code Pathfinder follows that chain and flags it. It also knows the difference between cursor.execute(query_string) (dangerous, argument position 0) and cursor.execute(sql, (params,)) (safe, the tuple at position 1 is the parameterization mechanism). That distinction alone eliminates a class of false positives that trips up most static analysis tools.

Security Implications

Potential attack scenarios if this vulnerability is exploited

1

Data Exfiltration

An attacker can read your entire database. UNION-based injection pulls data from other tables. Blind injection extracts it one bit at a time. Either way, one vulnerable endpoint is enough to leak every row in every table.

2

Authentication Bypass

The classic ' OR 1=1 -- in a login form. The query returns all users, the app logs in as the first one (usually admin). No brute force needed, no credentials required.

3

Data Manipulation

Injected INSERT, UPDATE, or DELETE statements can modify records, create backdoor accounts, or corrupt data. In financial applications, this means altered transactions.

4

Remote Code Execution

Some databases let you run OS commands: PostgreSQL has COPY, MySQL has INTO OUTFILE, MSSQL has xp_cmdshell. SQL injection on these systems can escalate to full server compromise.

How to Fix

Recommended remediation steps

  • 1Use parameterized queries -- cursor.execute("SELECT * FROM users WHERE name = ?", (username,)) -- not string concatenation
  • 2Switch to an ORM like SQLAlchemy if you're writing raw SQL frequently
  • 3Validate and type-check input before it reaches the database layer
  • 4Run database accounts with least privilege so a compromised query can't DROP tables
  • 5Log SQL queries in production to catch injection attempts early

Detection Scope

How Code Pathfinder analyzes your code for this vulnerability

This rule traces data across files (scope=global). It starts from Flask request methods -- request.args.get, request.form.get, request.values.get, request.get_json, request.cookies.get, request.headers.get -- and follows variables through assignments and function calls until they reach cursor.execute() or cursor.executemany(). The .tracks(0) setting means only the SQL string argument (position 0) triggers a finding. The parameter tuple at position 1 is ignored. Flows through escape() or escape_string() are treated as sanitized and excluded.

Compliance & Standards

Industry frameworks and regulations that require detection of this vulnerability

CWE Top 25
CWE-89 ranked #3 in 2023 Most Dangerous Software Weaknesses
OWASP Top 10
A03:2021 - Injection
PCI DSS v4.0
Requirement 6.2.4 -- prevent injection attacks
NIST SP 800-53
SI-10: Information Input Validation
SANS Top 25
Insecure Interaction Between Components

References

External resources and documentation

Similar Rules

Explore related security rules for Python

Frequently Asked Questions

Common questions about Flask SQL Injection via Tainted String

It follows the data. If a Flask route in app.py calls request.args.get('id'), passes that value to a helper function in db.py, and that function sticks it into cursor.execute() via string concatenation -- Code Pathfinder traces the full chain and flags it. Most real codebases split routes, services, and database code into separate files. This rule handles that.
Run: pathfinder ci --ruleset python/flask/PYTHON-FLASK-SEC-003 --project . It outputs SARIF, JSON, or CSV. If you're on GitHub, it can post inline review comments directly on pull requests pointing to the exact lines. No dashboard needed, no SARIF viewer -- just comments on the PR.
No. The .tracks(0) setting tells the engine that only the first argument to cursor.execute() matters -- that's the SQL string. The second argument is the parameter tuple (the safe one). So cursor.execute(sql, (user_input,)) won't trigger a finding even though user_input is tainted. This is the most common false positive in SQL injection scanning and this rule avoids it.
SQLite (sqlite3), MySQL (mysql-connector-python, PyMySQL), and PostgreSQL (psycopg2). The QueryType pattern *Cursor also catches cursor objects from other drivers that follow the DB-API 2.0 convention. For Django ORM-specific patterns like .raw() and .extra(), see PYTHON-DJANGO-SEC-002 and SEC-003.
PCI DSS v4.0 (Requirement 6.2.4), OWASP Top 10 (A03:2021), NIST SP 800-53 (SI-10), SOC 2 Type II (CC6.1), ISO 27001 (A.14.2.5), and the EU Cyber Resilience Act all require or recommend it. CWE-89 is #3 in the 2023 CWE Top 25. Running this rule in CI gives you auditable evidence for compliance reviews.
Yes. The rule file is plain Python. Fork it and add your custom input methods to from_sources, your database functions to to_sinks, or your validation logic to sanitized_by. You can version-control the modified rule alongside your app code.
Grep finds the word "execute" in your codebase. Code Pathfinder determines whether user-controlled data actually reaches that execute call. It follows variables through assignments, function calls, and file boundaries. It knows that escaped input is safe and that parameterized queries aren't vulnerable. Grep can't do any of that -- it either misses real bugs or drowns you in false positives.

New feature

Get these findings posted directly on your GitHub pull requests

The Flask SQL Injection via Tainted String rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.

See how it works