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-LANG-SEC-084 --project .About This Rule
Understanding the vulnerability and how it is detected
This rule detects the general pattern of SQL queries constructed using string formatting operations (f-strings, % operator, .format(), or string concatenation with +) and passed to any database cursor.execute() method, regardless of the specific database driver being used.
This covers drivers not addressed by more specific rules, including sqlite3, MySQL Connector, PyMySQL, cx_Oracle, and any custom database abstraction layer that exposes a cursor.execute() interface.
String-formatted SQL is the root cause of SQL injection vulnerabilities across all database drivers. The pattern is always the same: untrusted data embedded in the SQL string before it reaches the database engine, where the database interprets the embedded data as SQL syntax.
Security Implications
Potential attack scenarios if this vulnerability is exploited
Universal SQL Injection Risk
SQL injection via string-formatted queries is the most widespread database vulnerability class. Every database engine (PostgreSQL, MySQL, SQLite, Oracle, MSSQL) is affected when query strings are constructed from user input rather than using parameterized queries.
SQLite Local Database Exposure
Python applications using sqlite3 with string-formatted queries are vulnerable. Local SQLite databases may contain sensitive application state, user data, and credentials that can be exfiltrated or corrupted via SQL injection.
ORM Bypass via Raw SQL
Applications using ORMs often include escape hatches for raw SQL (Django's connection.cursor().execute(), SQLAlchemy's text()). String-formatted raw SQL in these contexts bypasses the ORM's parameterization protections.
Blind SQL Injection via Timing
Even when query results are not directly returned to the user (e.g., in background workers or event processors), time-based blind SQL injection using SLEEP() or pg_sleep() allows an attacker to extract data one bit at a time.
How to Fix
Recommended remediation steps
- 1Use the placeholder syntax appropriate to your database driver: ? for sqlite3, %s for psycopg2/MySQL/pg8000, $1/$2 for asyncpg, :name for SQLAlchemy text().
- 2Never construct SQL strings using f-strings, % operator, .format(), or string concatenation with user-controlled values.
- 3For ORMs, use the ORM's query building API rather than raw SQL; fall back to text() with explicit bindparams when raw SQL is needed.
- 4Validate and restrict dynamic SQL elements (ORDER BY directions, column names) to allowlists when they cannot be parameterized.
- 5Enable database-level audit logging to detect and alert on unusual query patterns that may indicate exploitation attempts.
Detection Scope
How Code Pathfinder analyzes your code for this vulnerability
This rule detects cursor.execute() and cursor.executemany() calls where the SQL string argument is built using string formatting operations. It covers any cursor-like object regardless of the specific database driver, serving as a catch-all for drivers not covered by more specific rules.
Compliance & Standards
Industry frameworks and regulations that require detection of this vulnerability
References
External resources and documentation
Similar Rules
Explore related security rules for Python
Frequently Asked Questions
Common questions about Formatted SQL Query Passed to cursor.execute()
New feature
Get these findings posted directly on your GitHub pull requests
The Formatted SQL Query Passed to cursor.execute() rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.