Description of the false negative
py/sql-injection seems to miss a sqlite3/PEP249 pattern where the cursor is created in one method, stored on self, and then used from another method.
This came up while looking at CVE-2025-55156:
In the real-world case, user-controlled input eventually reaches:
self.c.execute(f"SELECT id FROM links WHERE url IN ('{statuses}')")
Here is a simplified minimal example:
from flask import Flask, request # $ Source
import sqlite3
app = Flask(__name__)
class Repo:
def __init__(self):
self.conn = sqlite3.connect(":memory:")
self.c = self.conn.cursor()
def lookup(self, value):
self.c.execute(f"SELECT id FROM links WHERE url IN ('{value}')") # $ Alert
repo = Repo()
@app.route("/lookup")
def lookup():
value = request.args.get("value", "")
repo.lookup(value)
return "ok"
Expected behavior: an alert on the execute(...) call.
Actual behavior: no alert.
sqlite3 and PEP249 execute calls already seem to be modeled, so this looks like the receiver is not being recognized once the cursor is stored on an instance attribute.
This does not seem like a one-off pattern. Recent Python SQL injection CVEs keep showing the same basic issue: attacker-controlled data ends up in string-built SQL in sqlite/Python code, just through slightly different plumbing. A few examples:
- CVE-2026-21892 (Parsl): SQL built with Python
% formatting from a route parameter
- CVE-2026-33545 (MobSF): SQL built with Python
% formatting from attacker-controlled table names
- CVE-2025-67644 (LangGraph SQLite Checkpoint): SQL built with f-strings from attacker-controlled metadata keys
So even though this specific case uses self.c, the broader pattern is common enough that it would be useful if py/sql-injection covered it.
Description of the false negative
py/sql-injectionseems to miss a sqlite3/PEP249 pattern where the cursor is created in one method, stored onself, and then used from another method.This came up while looking at CVE-2025-55156:
In the real-world case, user-controlled input eventually reaches:
Here is a simplified minimal example:
Expected behavior: an alert on the
execute(...)call.Actual behavior: no alert.
sqlite3and PEP249executecalls already seem to be modeled, so this looks like the receiver is not being recognized once the cursor is stored on an instance attribute.This does not seem like a one-off pattern. Recent Python SQL injection CVEs keep showing the same basic issue: attacker-controlled data ends up in string-built SQL in sqlite/Python code, just through slightly different plumbing. A few examples:
%formatting from a route parameter%formatting from attacker-controlled table namesSo even though this specific case uses
self.c, the broader pattern is common enough that it would be useful ifpy/sql-injectioncovered it.