AST rules reference
All Vericto standard rules and how to configure them.
How rules work
When a query reaches the Vericto proxy, the parser builds the complete AST (Abstract Syntax Tree) of the query. The engine traverses the tree in depth searching for nodes that violate the conditions defined in each active rule.
The process is always the same:
- The SQL query reaches the proxy as plain text
- The engine selects the right parser for the dialect (
pg_queryfor Postgres,sqlparser-rsfor MySQL, Oracle and SQL Server). PostgreSQL and MySQL can be evaluated by the TCP proxy; Oracle and SQL Server, by the HTTP API. - The complete AST is built in memory
- The engine evaluates each active rule against the tree
- If any rule is violated, the query is blocked before reaching the database
Each rule defines exactly five properties:
- Code: unique identifier in the form
VERICTO-XXXorCUSTOM-XXX - AST condition: the node and property evaluated (e.g.
DeleteStmt > WhereClause = NULL) - Dialect(s): one or more SQL dialects the rule applies to
- Severity: one of the 5 CVSS levels — CRITICAL, HIGH, MEDIUM, LOW or INFORMATIONAL
- Suggested safe query: the corrected version of the query shown in the block log
AST parsing adds on average less than 0.4ms to the response time. The total proxy p99 (parsing + rule evaluation + network overhead) is under 2ms.
Severity levels
Vericto uses the 5-level CVSS taxonomy. The default action can be customised per workspace with an EnforcementPolicy.
| Severity | Acción por defecto | Behavior | Alerts |
|---|---|---|---|
| CRITICAL | BLOCK | Immediate block of the query. SQLSTATE 42501 to the client. | Alerta en tiempo real via webhook y dashboard |
| HIGH | BLOCK | Query blocked, fully recorded in the audit trail. | Alerta en tiempo real |
| MEDIUM | FLAG | The query is forwarded upstream. Logged and alerted on, but not blocked. | Alerta (FLAG) en dashboard y webhook |
| LOW | MONITOR | The query is forwarded. Recorded in telemetry only, no alert. | Solo telemetría / audit trail |
| INFORMATIONAL | MONITOR | The query is forwarded. Informational logging, no action. | Solo métricas |
Builder plans and above allow changing the behavior of any rule between block (block) and log-only (record without blocking). This is useful during the adoption period to observe traffic before enabling blocks.
CRITICAL rules
CRITICAL rules detect operations that can cause irreversible loss of massive amounts of data. They are active across all supported dialects unless otherwise indicated.
| Code | Name | AST Condition | Dialects | Suggested safe query |
|---|---|---|---|---|
VERICTO-001 |
DELETE without WHERE | DeleteStmt > WhereClause = NULL |
All | DELETE FROM {tabla} WHERE id = $1 |
VERICTO-003 |
DELETE with always-true WHERE | DeleteStmt > WhereClause = ALWAYS_TRUE (1=1, true) |
All | DELETE FROM {tabla} WHERE id = $1 |
VERICTO-010 |
DROP TABLE / DROP DATABASE | DropStmt (excluye DROP INDEX) |
All | Use versioned migrations (Flyway, Prisma Migrate, Alembic) |
VERICTO-011 |
TRUNCATE TABLE | TruncateStmt (nodo presente) |
All | DELETE FROM {tabla} WHERE created_at < NOW() - INTERVAL '90 days' |
VERICTO-012 |
DROP SCHEMA | DropStmt > ObjectType = SCHEMA |
All | Use versioned migrations |
VERICTO-030 |
UPDATE without WHERE (main tables) | UpdateStmt > WhereClause != Present |
All | UPDATE {tabla} SET {col} = $1 WHERE id = $2 |
VERICTO-042 |
UPDATE without WHERE clause | UpdateStmt > WhereClause = NULL (incluye WHERE trivial) |
All | UPDATE {tabla} SET {col} = $1 WHERE id = $2 |
VERICTO-080 |
COPY … TO/FROM PROGRAM | CopyStmt > program = true |
Postgres | Use COPY … TO/FROM STDIN or controlled file paths; never run shell commands from SQL |
VERICTO-081 |
DO anonymous code block | DoStmt (DO $$ … $$) |
Postgres | Move the logic to a reviewed function/migration instead of an anonymous block opaque to the parser |
HIGH rules
HIGH rules detect high-risk operations that can cause partial data loss or irreversible structural changes.
| Code | Name | AST Condition | Dialects | Suggested safe query |
|---|---|---|---|---|
VERICTO-002 |
DELETE with LIMIT 0 | DeleteStmt > LimitCount = 0 |
MySQL | DELETE FROM {tabla} WHERE id = $1 LIMIT 1 |
VERICTO-013 |
DROP INDEX without IF EXISTS | DropStmt > ObjectType = INDEX & IF_EXISTS = false |
All | DROP INDEX IF EXISTS {index_name} |
VERICTO-015 |
ALTER TABLE DROP COLUMN | AlterTableStmt > AlterTableCmd.subtype = DROP_COLUMN |
All | Use a soft delete with a deleted_at column, or a versioned migration |
VERICTO-016 |
ALTER TABLE RENAME | AlterTableStmt > Rename |
All | Use a migration tool and update all references first |
VERICTO-017 |
ALTER TABLE DROP CONSTRAINT | AlterTableStmt > DropConstraint (FK/PK/CHECK, DROP PRIMARY KEY) |
All | Drop the constraint only in a reviewed migration, after confirming no data or code depends on it |
VERICTO-018 |
ALTER TABLE ALTER COLUMN TYPE | AlterTableStmt > AlterColumnType |
All | Perform the type change in a phased migration (new column + backfill) to avoid a blocking rewrite or lossy cast |
VERICTO-019 |
ALTER TABLE DISABLE TRIGGER / RLS | AlterTableStmt > DisableTrigger | DisableRowSecurity |
All | Do not disable triggers or Row Level Security in production; if unavoidable, do it in a controlled window and re-enable them immediately |
VERICTO-031 |
UPDATE without WHERE nested in CTE | WithClause > UpdateStmt > WhereClause = NULL |
Postgres | Add a WHERE with a specific predicate to the UPDATE inside the CTE |
VERICTO-033 |
DELETE without WHERE in subquery/CTE | WithClause/SubLink > DeleteStmt > WhereClause = NULL |
All | Rewrite as DELETE with explicit JOIN and WHERE |
VERICTO-040 |
INSERT INTO … SELECT without filter | InsertStmt > source = SelectStmt |
All | INSERT INTO {tabla} SELECT ... WHERE <condición> LIMIT N |
VERICTO-070 |
Use of SLEEP() or PG_SLEEP() | FunctionCall > name = sleep|pg_sleep |
All | Remove the SLEEP call from the pipeline |
VERICTO-082 |
GRANT / REVOKE | GrantStmt (GRANT o REVOKE) |
All | Manage privileges through reviewed permission migrations, not from application queries |
VERICTO-083 |
MERGE | MergeStmt |
All | Constrain the MERGE with specific conditions; it can mass-mutate like an UPDATE/DELETE with no effective WHERE |
VERICTO-084 |
CREATE TABLE AS SELECT | CreateTableAsStmt / SELECT … INTO |
All | Add a WHERE/LIMIT to the source to avoid duplicating an entire table |
MEDIUM rules
MEDIUM rules detect query patterns that may indicate an AI agent operating without adequate context, or that could degrade database performance.
| Code | Name | AST Condition | Dialects | Suggested safe query |
|---|---|---|---|---|
VERICTO-050 |
SELECT without LIMIT | SelectStmt > LimitCount = NULL |
All | SELECT ... WHERE <condición> LIMIT 1000 |
VERICTO-051 |
SELECT * without WHERE | SelectStmt > TargetEntry = STAR & WhereClause = NULL |
All | SELECT col1, col2 FROM ... WHERE <condición> |
VERICTO-060 |
INSERT without explicit columns | InsertStmt > Cols = NULL |
All | INSERT INTO {tabla} (col1, col2) VALUES ($1, $2) |
VERICTO-061 |
INSERT batch with more than 10k rows | InsertStmt > ValuesList count > 10000 |
All | Split into batches of ≤1,000 rows with multiple INSERT or COPY |
SQL Injection rules
Deterministic detection of SQL injection patterns in LLM-generated queries. Unlike traditional WAFs based on text patterns, Vericto analyzes the AST, making detection immune to obfuscations that preserve semantics.
| Code | Name | AST Condition | Dialects | Why it avoids false positives |
|---|---|---|---|---|
VERICTO-090 |
OR tautology in WHERE | WhereClause > BoolExpr(OR) > always_true_branch |
All | An LLM generating legitimate queries never has a reason to include OR 1=1. They only exist in authentication-bypass attempts. |
VERICTO-090 coverage: applies to WHERE clauses in SELECT, DELETE, and UPDATE. Detects patterns like OR 1=1, OR 'a'='a', OR true, and nested tautologies like AND (condition OR 1=1).
Custom rules (YAML)
Builder plans and above allow defining custom rules in YAML. You can create rules that block specific operations on particular tables, without touching your application code.
Custom rules are evaluated after built-in rules with the same latency. Example custom rule:
id: CUSTOM-001
name: Bloquear UPDATE en tabla payments
severity: critical
dialect: postgres
condition:
node: UpdateStmt
relation: payments
message: "Las actualizaciones en la tabla payments requieren revisión manual"
The available condition fields are:
node: AST node type (e.g.UpdateStmt,DeleteStmt,SelectStmt)relation: exact table name (optional)where_null:trueto require the absence of a WHERE clausecolumns: list of columns that trigger the rule (optional)
Custom rules are hot-loaded without restarting the proxy. The propagation time is under 5 seconds from when you save the file in the dashboard.
Custom rules are managed from the workspace dashboard. From the CLI you can inspect the effective catalogue with vericto rules list and view a rule's detail with vericto rules show <CODE>.
Code index
| Code | Description | Severity | Acción por defecto |
|---|---|---|---|
VERICTO-001 | DELETE without WHERE clause | CRITICAL | BLOCK |
VERICTO-002 | DELETE with LIMIT 0 (MySQL) | HIGH | BLOCK |
VERICTO-003 | DELETE with always-true WHERE (1=1, true) | CRITICAL | BLOCK |
VERICTO-010 | DROP TABLE or DROP DATABASE | CRITICAL | BLOCK |
VERICTO-011 | TRUNCATE TABLE | CRITICAL | BLOCK |
VERICTO-012 | DROP SCHEMA | CRITICAL | BLOCK |
VERICTO-013 | DROP INDEX without IF EXISTS | HIGH | BLOCK |
VERICTO-015 | ALTER TABLE DROP COLUMN | HIGH | BLOCK |
VERICTO-016 | ALTER TABLE RENAME (table or column) | HIGH | BLOCK |
VERICTO-017 | ALTER TABLE DROP CONSTRAINT (FK/PK/CHECK) | HIGH | BLOCK |
VERICTO-018 | ALTER TABLE ALTER COLUMN TYPE | HIGH | BLOCK |
VERICTO-019 | ALTER TABLE DISABLE TRIGGER / RLS | HIGH | BLOCK |
VERICTO-030 | UPDATE without WHERE on main table | CRITICAL | BLOCK |
VERICTO-031 | UPDATE without WHERE inside CTE | HIGH | BLOCK |
VERICTO-033 | DELETE without WHERE in subquery or CTE | HIGH | BLOCK |
VERICTO-040 | INSERT INTO … SELECT without filter | HIGH | BLOCK |
VERICTO-042 | UPDATE without WHERE clause | CRITICAL | BLOCK |
VERICTO-050 | SELECT without LIMIT | MEDIUM | FLAG |
VERICTO-051 | SELECT * without WHERE clause | MEDIUM | FLAG |
VERICTO-060 | INSERT without explicit columns | LOW | MONITOR |
VERICTO-061 | INSERT batch with more than 10k rows | MEDIUM | FLAG |
VERICTO-070 | Use of SLEEP() or PG_SLEEP() | HIGH | BLOCK |
VERICTO-080 | COPY … TO/FROM PROGRAM (shell execution) | CRITICAL | BLOCK |
VERICTO-081 | DO anonymous code block | CRITICAL | BLOCK |
VERICTO-082 | GRANT / REVOKE | HIGH | BLOCK |
VERICTO-083 | MERGE | HIGH | BLOCK |
VERICTO-084 | CREATE TABLE AS SELECT | HIGH | BLOCK |
VERICTO-090 | SQL injection: OR tautology in WHERE (OR 1=1) | CRITICAL | BLOCK |