Skip to content

Replace keyword-regex read-only guard with AST-based SQL parsing (sqlglot) #6

Description

@cpenniman12

Follow-up to #1 (thanks @Mann1ng).

PR #1 closed the writable-CTE / subquery-DML / inline multi-statement bypasses by broadening _DESTRUCTIVE_PATTERN to match destructive keywords anywhere in the statement. That's the right security default, but it can false-positive on legitimate read-only queries that contain those keywords in string literals or identifiers, e.g.:

SELECT * FROM audit_log WHERE action = 'DELETE'
SELECT call FROM phone_log

The proper fix is recommendation #3 from docs/security-analysis.md: parse the statement into an AST (e.g. with sqlglot) and walk it for DML/DDL nodes anywhere in the tree — CTEs, subqueries, and multi-statement input included. That keeps the guard airtight without blocking legit queries.

Acceptance:

  • _is_read_only() uses AST inspection, regex kept only as a fallback for unparseable input (fail closed)
  • Existing TestReadOnlyBypassPrevention tests still pass
  • New tests: keyword-in-string-literal and keyword-as-identifier queries are allowed

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions