# Python Security
# Detects dynamic SQL passed into execute-like APIs.
id: python-sql-injection
name: SQL Injection Risk
severity: error
category: security
defect_class: injection
inline_tier: blocking
language: python

message: "Potential SQL injection sink — use parameterized queries"

description: |
  Dynamic SQL strings passed to execute-style APIs can enable SQL injection.
  Catches:
  - cursor.execute("SELECT " + var) — string concatenation (always unsafe)
  - cursor.execute(dynamic_sql) — variable with NO params arg (could be unsafe)
  
  Suppressed:
  - cursor.execute(query, params) — parameterized query (safe)
  - session.execute(select(...)) — SQLAlchemy ORM (safe by construction)
  - cursor.execute(f"...") — f-string with literal-only interpolation (rare false positive)
  
  ✅ FIX: use placeholders and parameter binding instead of string composition.

query: |
  (call
    function: (attribute
      object: (_) @OBJ
      attribute: (identifier) @FN)
    arguments: (argument_list
      [(binary_operator) (identifier) (call)] @SQL
      (_)*) @ARGS)

metavars:
  - OBJ
  - FN
  - SQL
  - ARGS

post_filter: py_sql_injection_sink

has_fix: false

tags:
  - python
  - security
  - sql-injection
  - cwe-89
  - owasp-a03

examples:
  bad: |
    cursor.execute("SELECT * FROM users WHERE id = " + user_id)
  
  good: |
    cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
