HeadyZhang/agent-audit

False positive: AGENT-041 flags agent `.execute()` calls as SQL injection

Open

#10 opened on Jul 4, 2026

View on GitHub
 (0 comments) (0 reactions) (0 assignees)Python (21 forks)github user discovery
bughelp wanted

Repository metrics

Stars
 (180 stars)
PR merge metrics
 (PR metrics pending)

Description

Summary

AGENT-041 (SQL Injection via String Interpolation) over-triggers on any .execute() method call, misclassifying agent-invocation patterns such as agent.execute(task) as SQL-injection sinks. The scanner's taint analysis reports sink_type: "sql_execution" for these calls even when the receiver is not a DB cursor or connection.

Version affected: 0.19.2 (also reproduces on prior releases sharing the same sink-matching logic).

Reproduction

Two-file minimal repro. Both files are scanned in a single invocation on agent-audit 0.19.2.

fp_case.py — should be clean (agent invocation, not SQL):

class Agent:
    def execute(self, task):
        return task


def delegate_task(target_agent, task):
    # This is agent invocation, not SQL execution.
    return target_agent.execute(task)

tp_control.py — positive control, should still fire (real SQL sink):

import sqlite3


def search_users(name: str):
    conn = sqlite3.connect("app.db")
    cursor = conn.cursor()
    # Real SQL injection sink (unparameterized f-string).
    cursor.execute(f"SELECT * FROM users WHERE name = '{name}'")
    return cursor.fetchall()

Command:

agent-audit scan . --format json

Actual output (both flagged, identical confidence and tier):

AGENT-041  @ tp_control.py:10  tier=BLOCK  conf=1.0  snippet='cursor.execute(f"SELECT * FROM users WHERE name = \'{name}\'")'
AGENT-041  @ fp_case.py:11     tier=BLOCK  conf=1.0  snippet='return target_agent.execute(task)'

The taint-analysis metadata attached to the false-positive finding is explicit about the misclassification:

{
  "dangerous_flows": [
    {
      "var": "task",
      "sink": "target_agent.execute",
      "sink_type": "sql_execution",
      "line": 11,
      "path": ["task"],
      "confidence": 0.85,
      "source": "user_input"
    }
  ],
  "sanitization_points": []
}

The scanner has classified target_agent.execute as sql_execution sink, though target_agent is not a DB cursor or connection.

Expected vs Actual

Case Expected Actual (0.19.2)
target_agent.execute(task) (agent invocation) Clean AGENT-041 BLOCK, conf 1.0
cursor.execute(f"SELECT ... {x}") (real SQL) AGENT-041 finding AGENT-041 BLOCK, conf 1.0

The positive control confirms the rule itself is functionally correct on real SQL sinks — this issue is scoped to over-triggering, not rule regression.

Root cause (preliminary)

The tainted-parameter check that produces this finding is _check_sql_tainted_param in packages/audit/agent_audit/scanners/python_scanner.py (defined at L3595). The only receiver-side gate is a method-name whitelist at L3608:

if simple_name not in ('execute', 'executemany', 'executescript'):
    return None

If the method name matches and the first argument is a function parameter (_current_function_params), the check emits a finding with the hard-coded sink_type: 'sql_execution' at L3627. No inspection of the receiver's type, imports, or origin is performed, so any object exposing an .execute(...) method — agent frameworks, task runners, ORM query builders, subprocess wrappers, concurrent.futures.Executor, etc. — is treated as a DB cursor.

The docstring at L3603-3606 confirms the check was written for the cursor.execute(query) pattern (introduced as the KNOWN-008 fix in v0.15.1), but the receiver-type premise was never enforced in code.

More broadly, this is a case where a syntactic signal (the method name .execute) is used as a proxy for a semantic property (the receiver is a SQL execution sink). The two coincide for DB-API cursors but diverge for any other object exposing .execute; resolving it reliably requires receiver-type information the current matcher does not track.

Environment

  • agent-audit 0.19.2 (installed via pip install agent-audit==0.19.2 from PyPI)
  • Python 3.14 (also reproduces on 3.9)
  • OS: macOS 15 (should be OS-agnostic)

Impact

Any codebase using .execute() as an agent invocation, task dispatch, or framework method — common in multi-agent frameworks (CrewAI, AutoGen, LangGraph, custom orchestrators) — will surface high-confidence BLOCK findings that require manual triage. This inflates FP rate on agent-oriented targets and lowers signal for real SQL sinks.

Contributor guide