Skip to content

Replace the lexical DuckDB guard with AST and resource admission #7

Description

@sounkou-bioinfo

Observed contract failure

At commit f3475cf000a379732d37cfb317754dec5ef2b4db, RhoConservativeSqlGuard accepts or rejects SQL from its first token plus a forbidden-keyword regular expression. That is not a statement parser and does not establish the package's read-only claim.

An executed local canary used a fresh in-memory DuckDB database and a temporary output directory:

sql <- sprintf(
  "SELECT 1 AS accepted_prefix; EXPORT DATABASE '%s' (FORMAT PARQUET)",
  outdir
)
decision <- rho_check_readonly_sql(sql)
result <- rho_await(rho_sql_all(conn, sql), timeout = 5000)

Observed:

decision=RhoSqlAccepted
output_dir=TRUE
files=canary.parquet,load.sql,schema.sql

The same lexical design omits other statement families such as SET, CALL, and broad PRAGMA behavior. A SELECT statement can also invoke table functions that read undeclared paths or remote resources. “Starts with SELECT” and “contains no listed write word” are therefore neither single-statement validation nor resource admission.

Required contract

Use DuckDB's parser/AST serialization (for example json_serialize_sql) as the semantic authority:

  1. Parse first and require exactly one statement.
  2. Admit explicit read-only statement and expression classes; reject unknown AST nodes fail-closed.
  3. Inspect relation references, table functions, extension operations, path/URI literals, secrets, and external-access functions against host-supplied capabilities.
  4. Execute the same admitted statement, not a separately reinterpreted string.
  5. Keep SQL syntax admission separate from relation/path/network authority.
  6. Return a typed rejection containing the rejected AST class and policy reason without echoing secrets.

This must land before rho.bio.agent exposes model-authored query execution.

Acceptance evidence

  • comments and quoted keywords do not cause false decisions;
  • a second statement is rejected regardless of its spelling;
  • EXPORT, IMPORT, COPY, ATTACH, INSTALL, LOAD, mutating PRAGMA, SET, and effectful CALL cases fail closed;
  • CTEs and ordinary nested read-only queries remain accepted;
  • undeclared relations and path/remote table functions are rejected by capability policy;
  • parser failure and a newly encountered AST class produce typed rejection;
  • an executed canary proves no filesystem artifact or session mutation occurs after rejection.

Prepared in Pi using GPT Sol 5.6 High; the canary above was executed locally and cleaned up before filing.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions