VNX-619: Database cursor opened in a function and never closed (Python DB-API)
Reports a Python DB-API cursor opened in a function and never closed, used as a context manager, returned, or released with its connection.
| Field | Value |
|---|---|
| Rule ID | VNX-619 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | Python |
| CWE | CWE-619 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:L/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L |
| Tags | dangling-db-cursor resource-leak cwe-619 |
| Source | internal/sast/rules/vnx-619.rego |
Overview
VNX-619 maps to CWE-619: Dangling Database Cursor (‘Cursor Injection’). A cursor left open keeps its result set and server-side resources (a named PostgreSQL cursor, a MySQL unbuffered result) until garbage collection, and a pooled connection goes to the next request with it still attached.
What Gets Flagged
def find_user(db_path, user_id):
conn = sqlite3.connect(db_path)
cur = conn.cursor() # flagged: never closed
cur.execute("SELECT name FROM users WHERE id = ?", (user_id,))
return cur.fetchone()
def report(request):
cursor = connection.cursor() # flagged (Django)
cursor.execute("SELECT id FROM shop_order")
return JsonResponse({"rows": cursor.fetchall()})
Not flagged: with conn.cursor() as cur:, with closing(cur):, cur.close() later in the function or in a finally block, a closed connection, a cursor returned or yielded to the caller, a cursor stored on self, module-level scripts, comments and string literals.
Remediation
Open cursors in a with block, or close them in finally:
with conn.cursor() as cur:
cur.execute("SELECT name FROM users WHERE id = %s", (user_id,))
return cur.fetchone()
References
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-619
// nosec vnx-619
Or record an ignore rule: vulnetix ignore add --rule vnx-619 --reason "...". See Suppressing findings.