42 rules. None uses ML.

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:

  1. The SQL query reaches the proxy as plain text
  2. The engine selects the right parser for the dialect (pg_query for Postgres, sqlparser-rs for MySQL, Oracle, SQL Server, Snowflake and SQLite). PostgreSQL and MySQL can be evaluated by the TCP proxy; Oracle, SQL Server, Snowflake and SQLite, by the HTTP API.
  3. The complete AST is built in memory
  4. The engine evaluates each active rule against the tree
  5. 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-XXX or CUSTOM-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

What the proxy adds is measured, with its conditions, in Measured latency: 0.42 ms at p50 on PostgreSQL with 1 connection (staging, October 16, 2026). A measurement, not a guarantee.

Coverage by incident family

42 rules · 6 dialects · 8 incident families · 52 scenarios. A scenario is a concrete SQL pattern a rule catches, with an example we ran through vericto-engine 4.1.1 (the 42 standard rules and the default policy) on October 16, 2026.

How it is counted: a pattern is one scenario, even when it is detected on several dialects or at several positions of the statement. Each scenario has one primary family and one primary origin, so the per-family and per-origin counts add up to the total. Engine limits that are not rules (nesting and size) are not counted. Two scenarios need configuration: VERICTO-085 (sensitive columns) and VERICTO-087 (agent allowlists).

The filter also applies to the rule tables on this page.

FamilyScenariosRules
Mass data deletion or rewrite10001 003 011 030 031 033 040 042 083 094
Schema destruction7010 012 013 015 016 094
Disabling protections6017 019 099
Exfiltration and data leakage10040 082 084 085 087 091 092 093 098 100 101
Code execution and privilege escalation6080 081 082 096 097 100
Injection and evasion4010 012 070 086 090
Performance and cost5050 051 061 095
Recovery and data quality4002 018 060 094

Scenarios by origin

The same rule stops a person's mistake, an ORM bug and a compromised agent's attack. Each scenario is counted under its most typical origin:

OriginScenarios
Developer or DBA console5
Application or ORM bug7
CI/CD migration9
AI agent or text-to-SQL9
SQL-injection attacker9
Insider or compromised agent13

The 52 scenarios

IDScenarioRulesDefault actionDialectsOrigin
S01 DELETE without WHERE VERICTO-001 block All 6 AI agent or text-to-SQL
S02 DELETE with an always-true WHERE (1=1, id = id, SQLite '1abc') VERICTO-003 block All 6 AI agent or text-to-SQL
S03 UPDATE without WHERE VERICTO-030 VERICTO-042 block All 6 Developer or DBA console
S04 UPDATE with an always-true WHERE (a query builder with an empty filter) VERICTO-030 VERICTO-042 block All 6 Application or ORM bug
S05 WHERE-less UPDATE inside a data-modifying CTE VERICTO-031 VERICTO-030 VERICTO-042 block PostgreSQL AI agent or text-to-SQL
S06 WHERE-less DELETE hidden in a CTE under a SELECT VERICTO-001 VERICTO-033 block PostgreSQL Insider or compromised agent
S07 Mass DELETE or UPDATE in a trigger body, run on every write VERICTO-001 VERICTO-003 VERICTO-030 block SQLite CI/CD migration
S08 TRUNCATE VERICTO-011 block PostgreSQL, MySQL, Oracle, SQL Server, Snowflake Developer or DBA console
S09 MERGE that can match every row (every MERGE is blocked by default) VERICTO-083 block PostgreSQL, Oracle, SQL Server, Snowflake Application or ORM bug
S10 Snowflake INSERT OVERWRITE replaces every row VERICTO-094 VERICTO-040 block Snowflake CI/CD migration
S11 DROP TABLE VERICTO-010 block All 6 AI agent or text-to-SQL
S12 DROP DATABASE VERICTO-010 block PostgreSQL, MySQL, Oracle, SQL Server, Snowflake Developer or DBA console
S13 DROP SCHEMA … CASCADE (an ORM "reset") VERICTO-012 block PostgreSQL, MySQL, Oracle, SQL Server, Snowflake CI/CD migration
S14 ALTER TABLE … DROP COLUMN (a schema push) VERICTO-015 block All 6 CI/CD migration
S15 Rename a table or column (Snowflake SWAP WITH too) VERICTO-016 block All 6 CI/CD migration
S16 DROP INDEX without IF EXISTS VERICTO-013 block All 6 CI/CD migration
S17 Snowflake CREATE OR REPLACE drops the existing table, schema or database VERICTO-094 block Snowflake AI agent or text-to-SQL
S18 Disable row-level security VERICTO-019 block PostgreSQL Insider or compromised agent
S19 Disable the triggers of a table (audit triggers included) VERICTO-019 block PostgreSQL Developer or DBA console
S20 DROP TRIGGER VERICTO-019 block PostgreSQL, MySQL, Oracle, SQL Server, SQLite CI/CD migration
S21 Drop a foreign key, primary key or CHECK constraint VERICTO-017 block PostgreSQL, MySQL, Oracle, SQL Server, Snowflake CI/CD migration
S22 Snowflake: drop a row access policy or unset a masking policy VERICTO-019 block Snowflake Insider or compromised agent
S23 SQLite PRAGMA that turns a safety off (writable_schema, foreign_keys = OFF, journal_mode = OFF …) VERICTO-099 block SQLite Application or ORM bug
S24 INSERT … SELECT copies a whole table somewhere readable VERICTO-040 block All 6 SQL-injection attacker
S25 CREATE TABLE AS SELECT bulk copy (Snowflake CLONE too) VERICTO-084 block PostgreSQL, Snowflake, SQLite Insider or compromised agent
S26 Read of a column you tagged as sensitive (block, mask or flag) needs configuration VERICTO-085 per tag All 6 AI agent or text-to-SQL
S27 An agent touches a table or column outside its allowlist needs configuration VERICTO-087 block (enforce) / flag (observe) All 6 Insider or compromised agent
S28 Snowflake unload to a stage or external location, GET, PUT VERICTO-091 block Snowflake Insider or compromised agent
S29 Snowflake external stage, integration or external function VERICTO-092 block Snowflake Insider or compromised agent
S30 Snowflake share to another account VERICTO-093 VERICTO-082 block Snowflake Insider or compromised agent
S31 SQLite ATTACH DATABASE opens or creates another file VERICTO-098 block SQLite SQL-injection attacker
S32 SQLite VACUUM INTO writes a full copy of the database VERICTO-101 block SQLite Insider or compromised agent
S33 SQLite readfile(), writefile() or edit(): the file system from SQL VERICTO-100 block SQLite SQL-injection attacker
S34 COPY … TO/FROM PROGRAM runs a shell command on the database host VERICTO-080 block PostgreSQL SQL-injection attacker
S35 Anonymous code block that hides statements (DO, Snowflake EXECUTE IMMEDIATE or scripting) VERICTO-081 block PostgreSQL, Snowflake AI agent or text-to-SQL
S36 GRANT or REVOKE VERICTO-082 block PostgreSQL, MySQL, Oracle, SQL Server, Snowflake Insider or compromised agent
S37 Snowflake switch to an administrative role VERICTO-097 block Snowflake Insider or compromised agent
S38 Snowflake code that runs later, outside any check (task, procedure, stream, pipe, alert) VERICTO-096 block Snowflake Insider or compromised agent
S39 SQLite load_extension() or fts3_tokenizer(): native code VERICTO-100 block SQLite SQL-injection attacker
S40 OR tautology in a WHERE (' OR 1=1) VERICTO-090 block All 6 SQL-injection attacker
S41 Time-based blind probe (pg_sleep, SLEEP, system$wait, SQLite randomblob) VERICTO-070 block All 6 SQL-injection attacker
S42 Stacked statement after a read-only wrapper (COMMIT; DROP …) VERICTO-012 VERICTO-010 block All 6 SQL-injection attacker
S43 MySQL executable comment or escape the engine cannot resolve to one statement VERICTO-086 block MySQL SQL-injection attacker
S44 Unbounded SELECT (no LIMIT, FETCH or TOP) VERICTO-050 flag All 6 AI agent or text-to-SQL
S45 SELECT * without WHERE VERICTO-051 flag All 6 Application or ORM bug
S46 INSERT batch over 10,000 rows VERICTO-061 flag All 6 Application or ORM bug
S47 COPY FROM a file on the database server VERICTO-061 flag PostgreSQL Developer or DBA console
S48 Snowflake warehouse resize or never-suspend VERICTO-095 block Snowflake AI agent or text-to-SQL
S49 Lossy or blocking column type change VERICTO-018 block PostgreSQL, Snowflake CI/CD migration
S50 INSERT without an explicit column list VERICTO-060 monitor All 6 Application or ORM bug
S51 DELETE … LIMIT 0 (a misconfigured scope) VERICTO-002 block MySQL, SQLite Application or ORM bug
S52 Snowflake recovery reduced (retention 0, UNDROP, ALTER ACCOUNT) VERICTO-094 block Snowflake Insider or compromised agent

Where it is detected

The rules are evaluated on every statement the engine finds, not only the top one: CTEs (and data-modifying CTEs, on PostgreSQL), derived tables and JOIN subqueries, scalar subqueries in the projection, UNION arms, the source of an INSERT … SELECT, every statement of a multi-statement string, the body of a SQLite trigger and of a Snowflake scripting block. PostgreSQL and SQLite also read the IN and EXISTS subqueries of a SELECT WHERE, and SQLite reads every clause. VERICTO-050 is the deliberate exception: it only looks at the query the client receives.

A DELETE hidden in a CTE, a tautology in a derived table or a UNION arm, a DROP stacked after a COMMIT, an INSERT … SELECT inside a SQLite trigger: the engine walks the syntax tree down to 50 levels and evaluates every statement of a multi-statement string. Anything deeper, or anything the parser cannot read, does not pass silently: it is recorded as a parse error, and blocked if you choose parse_error: block.

About 950 chained NOTs, roughly 4 KB of SQL, overflow a recursive parser's stack and kill the process running it. Vericto measures the text's nesting before parsing it and refuses anything past 200 levels: the proxy stays up and the statement is recorded as too complex (blocked under parse_error: block).

What vericto-engine 4.1.1 does not read yet

  • On MySQL, Oracle, SQL Server and Snowflake, a tautology, a sleep call or a SELECT * inside an IN or EXISTS subquery of the WHERE (PostgreSQL and SQLite do read it).
  • A subquery inside the WHERE of an UPDATE or DELETE: only SQLite reads it.
  • Outside SQLite, a sleep call inside IS NULL, CASE, ORDER BY or UPDATE … SET (in the projection, in a WHERE comparison, in CTEs and in derived tables it is detected).
  • VERICTO-084 does not report MySQL or Oracle CREATE TABLE … SELECT, nor SELECT … INTO on PostgreSQL and SQL Server; VERICTO-018 does not report MySQL MODIFY or CHANGE COLUMN.
  • Native forms the parser does not read (SQL Server GRANT CONTROL, Oracle system privileges, SQL Server DISABLE TRIGGER) are parse errors: forwarded and recorded by default, blocked under parse_error: block.

In 6 public incidents we documented, the destructive SQL (the published statement, or the one the mechanism implies) is BLOCKED by these rules when the connection goes through Vericto: a DELETE FROM on every production table, DROP TABLE requested from text-to-SQL frameworks (two CVEs), a schema push that dropped tables, COPY … TO PROGRAM on a "read-only" MCP server and a COMMIT; DROP SCHEMA that escaped a read-only transaction. The cases, with their sources.

Severity levels

Vericto uses the 5-level CVSS taxonomy. The default action can be customised per workspace with an EnforcementPolicy.

Severity Default action Behavior Alerts
CRITICAL BLOCK Immediate block of the query. SQLSTATE 42501 to the client. Real-time alert via webhook and dashboard
HIGH BLOCK Query blocked, fully recorded in the audit trail. Real-time alert
MEDIUM FLAG The query is forwarded upstream. Logged and alerted on, but not blocked. Alert (FLAG) in dashboard and webhook
LOW MONITOR The query is forwarded. Recorded in telemetry only, no alert. Telemetry / audit trail only
INFORMATIONAL MONITOR The query is forwarded. Informational logging, no action. Metrics only

On every plan, the workspace owner and admins can customize the EnforcementPolicy (change the action per severity or turn on observe mode). This is useful during adoption 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 (excludes DROP INDEX) All Use versioned migrations (Flyway, Prisma Migrate, Alembic)
VERICTO-011 TRUNCATE TABLE TruncateStmt (nodo presente) All but SQLite DELETE FROM {tabla} WHERE created_at < NOW() - INTERVAL '90 days'
VERICTO-012 DROP SCHEMA DropStmt > ObjectType = SCHEMA All but SQLite 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 (includes trivial WHERE) 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
VERICTO-086 SQL text that MySQL and the engine would read differently Text that cannot be resolved with certainty to a single statement because of how MySQL reads its comments and string escapes MySQL Always enforced: it cannot be disabled or downgraded. Send SQL without ambiguous comments or escapes

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, SQLite 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 but SQLite 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 Postgres, Snowflake 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 Postgres (DISABLE TRIGGER, RLS), Snowflake (policies); DROP TRIGGER on all but Snowflake 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 Postgres 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 or REVOKE) All but SQLite Manage privileges through reviewed permission migrations, not from application queries
VERICTO-083 MERGE MergeStmt Postgres, Oracle, SQL Server, Snowflake 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 Postgres, Snowflake, SQLite Add a WHERE/LIMIT to the source to avoid duplicating an entire table
VERICTO-085 Read of a sensitive column A projection derived from a tagged column (SELECT, RETURNING, COPY … TO) All Decided by each database's sensitive columns: list the columns instead of SELECT * and do not project the tagged ones
VERICTO-087 Access outside the agent's allowlist Any reference (projection, predicate, subquery, write target) the identity's allowlist does not grant, or DDL All Decided by the agent allowlist: it blocks under enforce and flags under observe

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 What does not trigger it
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).

Snowflake rules

Seven rules for Snowflake (vericto-engine 4.0.0). They read only what the Snowflake parser records, so they never fire on another dialect. Snowflake has no TCP proxy: these rules apply in the HTTP API, the CLI in CI/CD and MCP.

Code Name AST Condition Dialects Suggested safe query
VERICTO-091 Stage unload or file transfer COPY INTO @stage | 's3://…' | 'azure://…' | 'gcs://…', GET, PUT Snowflake Load from the stage (COPY INTO table FROM @stage); data unloads go through a reviewed process outside the application
VERICTO-092 External stage or integration CREATE|ALTER STAGE … URL = | CREDENTIALS, CREATE STORAGE | EXTERNAL ACCESS | API INTEGRATION, CREATE EXTERNAL FUNCTION Snowflake Use an internal stage; integrations and external stages are created in reviewed migrations
VERICTO-093 Share to another account GRANT … TO SHARE, CREATE SHARE, ALTER SHARE … ADD | SET ACCOUNTS, CREATE LISTING Snowflake Sharing data is an account decision: make it outside application SQL
VERICTO-094 Recovery reduced DATA_RETENTION_TIME_IN_DAYS = 0, CREATE OR REPLACE TABLE | SCHEMA | DATABASE, INSERT OVERWRITE, UNDROP, ALTER ACCOUNT Snowflake CREATE TABLE IF NOT EXISTS and a retention above 0; in CI it is flagged like a DROP TABLE
VERICTO-095 Warehouse cost CREATE|ALTER WAREHOUSE … WAREHOUSE_SIZE | MAX_CLUSTER_COUNT | AUTO_SUSPEND = 0, ALTER RESOURCE MONITOR Snowflake Create the warehouse with the defaults or an AUTO_SUSPEND above 0; size changes go in a reviewed migration
VERICTO-096 Code that runs later CREATE TASK | STREAM | PIPE | PROCEDURE | FUNCTION | ALERT, ALTER TASK … RESUME | MODIFY AS, EXECUTE TASK Snowflake Create tasks and procedures in reviewed migrations; from the application, call them with CALL
VERICTO-097 Role switch USE ROLE, USE SECONDARY ROLES ALL | <roles> Snowflake Connect with the role the application needs; USE SECONDARY ROLES NONE is not reported

SQLite rules

Four rules for SQLite (vericto-engine 4.1.0). They read only what the SQLite parser records, so they never fire on another dialect. SQLite is a library inside your application, with no server and no network protocol, so there is no TCP proxy: these rules apply in the Python and Node SDKs, the Runtime API, the CLI in CI/CD and MCP (SQLite).

Code Name AST Condition Dialects Suggested safe query
VERICTO-098 SQLite ATTACH DATABASE ATTACH [DATABASE] <file> AS <name> SQLite Do not attach files from the application; open each database with its own connection. DETACH is not reported
VERICTO-099 SQLite safety PRAGMA PRAGMA writable_schema | trusted_schema = ON | foreign_keys = OFF | ignore_check_constraints = ON | journal_mode = OFF | synchronous = OFF | cell_size_check = OFF | query_only = OFF | schema_version = n | temp_store_directory SQLite Reads (PRAGMA table_info(t)) and safe settings (foreign_keys = ON, journal_mode = WAL) are not reported; in CI it is flagged like a DROP TABLE
VERICTO-100 SQLite extension loading or file functions load_extension(), fts3_tokenizer(), readfile(), writefile(), edit(), fsdir(), zipfile() en cualquier cláusula SQLite Load extensions when the connection opens, from application code, never from SQL
VERICTO-101 SQLite VACUUM INTO VACUUM [schema] INTO <file> SQLite A plain VACUUM is not reported; backups go through a reviewed process (the SQLite backup API)

The standard rules on SQLite

  • Apply as they are (15): VERICTO-001, 002 (DELETE … LIMIT 0, on builds with SQLITE_ENABLE_UPDATE_DELETE_LIMIT), 010, 013, 015, 016, 019 (DROP TRIGGER; SQLite has no DISABLE TRIGGER or RLS), 030, 040, 042, 050, 051, 060, 061 (batches over 10,000 rows; there is no COPY) and 084 (CREATE TABLE … AS SELECT).
  • With a SQLite variant (5): 003 and 090 (in SQLite a string with a non-zero numeric prefix, such as WHERE '1abc', is true; also as a double-quoted name, "1", under DQS), 070 (SQLite has no sleep: randomblob(N) or zeroblob(N) with a constant N of 10,000,000 bytes or more, the time-based injection probe, is detected), 085 (SQLite masks; hash blocks) and 087 (case-insensitive names, main as the default schema).
  • Do not apply, never fire on SQLite (11): 011 (TRUNCATE), 012 (DROP SCHEMA), 017 (DROP CONSTRAINT), 018 (ALTER COLUMN … TYPE), 031 and 033 (SQLite has no data-modifying CTEs), 080 (COPY … PROGRAM), 081 (DO blocks), 082 (GRANT/REVOKE), 083 (MERGE) and 086 (MySQL's lexical ambiguity). SQLite rejects those statements.

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. The YAML has node_type at the top level and the predicates inside condition:

rule: block-update-on-payments
node_type: UpdateStmt
condition:
  relation: payments

There are eight condition predicates: relation, where_clause, where_always_true, target_list, has_limit, func_name, object_type and alter_kind. Name, severity and dialect do not belong in the YAML: they are form and API fields.

The full reference — the eight node types, the eight predicates and copy-ready examples — is in the custom rules documentation.

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>.

Measured latency, with its conditions

Measured on staging on October 16, 2026: vericto-proxy 4.12.0 on a 0.25 vCPU / 512 MB ARM64 Fargate task, Aurora Serverless v2 pre-warmed to 10 ACU (PostgreSQL 16.9 and MySQL 8.0.39), TLS on both legs, enforce mode with the standard catalogue, and an ORM-style mix of small legitimate statements: key lookups, joins, IN lists, single-row UPDATE and INSERT, transactions, plus flagged and blocked statements. The direct path to the database was measured in the same run, with 3 repetitions per cell.

  • Added latency at p50 (proxy minus direct): PostgreSQL 0.42 ms at 1 connection, 0.50 ms at 4 and 0.86 ms at 16 (about 2,000 statements/s); MySQL 0.34 to 0.36 ms at 1 to 32 connections.
  • Added at p99, below proxy saturation: PostgreSQL 0.51 ms at 1 connection; MySQL 1.9 ms or less at 1 and 4 connections. For reference, over the direct path a key lookup takes 0.28 ms at p50.
  • Capacity: one 0.25 vCPU instance handled about 2,000 PostgreSQL statements/s before its CPU saturated; beyond that, the queue grows inside the proxy. For more traffic, give it more CPU or add instances behind the load balancer (the latter was not measured here).
  • Statement size: what is added grows with size. On PostgreSQL, a 10-row INSERT adds 0.58 ms at p50; 100 rows, 1.05 ms; 500 rows, 3.07 ms; 1,000 rows, 5.54 ms.
  • Rule evaluation alone, no network: p99 of 100 µs or less for the OLTP statements (vericto-engine 3.9.0 on 0.25 vCPU ARM64).
  • Telemetry: 0 of the 1,222,254 events sent to the control plane during the session were lost.

It is a measurement under those conditions, not a guarantee: your network, instance size and statements change the result. Observe mode and masked columns were not measured in this session.

Code index

Code Description Severity Default action
VERICTO-001DELETE without WHERE clauseCRITICALBLOCK
VERICTO-002DELETE with LIMIT 0 (MySQL, SQLite)HIGHBLOCK
VERICTO-003DELETE with always-true WHERE (1=1, true)CRITICALBLOCK
VERICTO-010DROP TABLE or DROP DATABASECRITICALBLOCK
VERICTO-011TRUNCATE TABLECRITICALBLOCK
VERICTO-012DROP SCHEMACRITICALBLOCK
VERICTO-013DROP INDEX without IF EXISTSHIGHBLOCK
VERICTO-015ALTER TABLE DROP COLUMNHIGHBLOCK
VERICTO-016ALTER TABLE RENAME (table or column)HIGHBLOCK
VERICTO-017ALTER TABLE DROP CONSTRAINT (FK/PK/CHECK)HIGHBLOCK
VERICTO-018ALTER TABLE ALTER COLUMN TYPEHIGHBLOCK
VERICTO-019ALTER TABLE DISABLE TRIGGER / RLSHIGHBLOCK
VERICTO-030UPDATE without WHERE on main tableCRITICALBLOCK
VERICTO-031UPDATE without WHERE inside CTEHIGHBLOCK
VERICTO-033DELETE without WHERE in subquery or CTEHIGHBLOCK
VERICTO-040INSERT INTO … SELECT without filterHIGHBLOCK
VERICTO-042UPDATE without WHERE clauseCRITICALBLOCK
VERICTO-050SELECT without LIMITMEDIUMFLAG
VERICTO-051SELECT * without WHERE clauseMEDIUMFLAG
VERICTO-060INSERT without explicit columnsLOWMONITOR
VERICTO-061INSERT batch with more than 10k rowsMEDIUMFLAG
VERICTO-070Use of SLEEP() or PG_SLEEP()HIGHBLOCK
VERICTO-080COPY … TO/FROM PROGRAM (shell execution)CRITICALBLOCK
VERICTO-081DO anonymous code blockCRITICALBLOCK
VERICTO-082GRANT / REVOKEHIGHBLOCK
VERICTO-083MERGEHIGHBLOCK
VERICTO-084CREATE TABLE AS SELECTHIGHBLOCK
VERICTO-085Read of a sensitive columnHIGHPER TAG
VERICTO-086SQL text that MySQL and the engine would read differently (MySQL)CRITICALBLOCK
VERICTO-087Access outside the agent's allowlistHIGHBLOCK (ENFORCE) / FLAG (OBSERVE)
VERICTO-090SQL injection: OR tautology in WHERE (OR 1=1)CRITICALBLOCK
VERICTO-091Stage unload or file transfer (Snowflake)CRITICALBLOCK
VERICTO-092External stage or integration (Snowflake)CRITICALBLOCK
VERICTO-093Share to another account (Snowflake)CRITICALBLOCK
VERICTO-094Recovery reduced (Snowflake)HIGHBLOCK
VERICTO-095Warehouse cost (Snowflake)HIGHBLOCK
VERICTO-096Code that runs later (Snowflake)HIGHBLOCK
VERICTO-097Role switch (Snowflake)HIGHBLOCK
VERICTO-098ATTACH DATABASE (SQLite)HIGHBLOCK
VERICTO-099Safety PRAGMA (SQLite)HIGHBLOCK
VERICTO-100Extension loading or file functions (SQLite)CRITICALBLOCK
VERICTO-101VACUUM INTO (SQLite)HIGHBLOCK