What does the QueryShield policy DSL look like (examples)?
The policy DSL is YAML, version-controlled in your repo, evaluated per request against the parsed AST + request context. A worked example:
version: 1
agents:
customer-support:
statement_types: [SELECT] # default-deny on writes
max_rows: 100 # auto-inject LIMIT if missing
timeout_ms: 2000
tables: orders: require: "tenant_id = :ctx.tenant_id" allowed_columns: [id, user_id, total, status, created_at] users: require: "id = :ctx.user_id AND tenant_id = :ctx.tenant_id" allowed_columns: [id, email, name, tenant_id] denied_columns: [password_hash, api_token, mfa_secret] tickets: require: "tenant_id = :ctx.tenant_id"
deny: functions: [pg_sleep, SLEEP, WAITFOR, BENCHMARK, COPY, EXPORT_DATA] operations: [UNION, INTERSECT] # close exfil class entirely tables_outside_allowlist: true
reporting-agent: statement_types: [SELECT] tables: v_reporting_aggregate: # views only require: "tenant_id = :ctx.tenant_id" rewrite: inject_missing_predicates: true # auto-inject required clauses
refunds-agent: statement_types: [SELECT, UPDATE] require_idempotency_key: true tables: refunds: require: "tenant_id = :ctx.tenant_id AND status = 'pending'" update_allowed_columns: [status, refunded_at] ```
Key features:
requirepredicates are enforced on everySelectStmtbranch including UNIONs and CTEs.rewrite.inject_missing_predicatesauto-adds the binding clause rather than rejecting, useful for cooperative LLM workflows.:ctx.*placeholders bind to per-request context the calling code passes in.- Inheritance: a
baseagent can be extended with overrides for tenant-specific deployments. - Audit-friendly: every rejection logs
rule=tables_outside_allowlist,rule=missing_required_predicate, etc.