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