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:
- 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, 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. - 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
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.
| Family | Scenarios | Rules |
|---|---|---|
| Mass data deletion or rewrite | 10 | 001 003 011 030 031 033 040 042 083 094 |
| Schema destruction | 7 | 010 012 013 015 016 094 |
| Disabling protections | 6 | 017 019 099 |
| Exfiltration and data leakage | 10 | 040 082 084 085 087 091 092 093 098 100 101 |
| Code execution and privilege escalation | 6 | 080 081 082 096 097 100 |
| Injection and evasion | 4 | 010 012 070 086 090 |
| Performance and cost | 5 | 050 051 061 095 |
| Recovery and data quality | 4 | 002 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:
| Origin | Scenarios |
|---|---|
| Developer or DBA console | 5 |
| Application or ORM bug | 7 |
| CI/CD migration | 9 |
| AI agent or text-to-SQL | 9 |
| SQL-injection attacker | 9 |
| Insider or compromised agent | 13 |
The 52 scenarios
| ID | Scenario | Rules | Default action | Dialects | Origin |
|---|---|---|---|---|---|
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 BYorUPDATE … 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, norSELECT … INTOon PostgreSQL and SQL Server; VERICTO-018 does not report MySQLMODIFYorCHANGE COLUMN. - Native forms the parser does not read (SQL Server
GRANT CONTROL, Oracle system privileges, SQL ServerDISABLE TRIGGER) are parse errors: forwarded and recorded by default, blocked underparse_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 withSQLITE_ENABLE_UPDATE_DELETE_LIMIT),010,013,015,016,019(DROP TRIGGER; SQLite has noDISABLE TRIGGERor RLS),030,040,042,050,051,060,061(batches over 10,000 rows; there is noCOPY) and084(CREATE TABLE … AS SELECT). - With a SQLite variant (5):
003and090(in SQLite a string with a non-zero numeric prefix, such asWHERE '1abc', is true; also as a double-quoted name,"1", under DQS),070(SQLite has nosleep:randomblob(N)orzeroblob(N)with a constant N of 10,000,000 bytes or more, the time-based injection probe, is detected),085(SQLite masks;hashblocks) and087(case-insensitive names,mainas the default schema). - Do not apply, never fire on SQLite (11):
011(TRUNCATE),012(DROP SCHEMA),017(DROP CONSTRAINT),018(ALTER COLUMN … TYPE),031and033(SQLite has no data-modifying CTEs),080(COPY … PROGRAM),081(DOblocks),082(GRANT/REVOKE),083(MERGE) and086(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-001 | DELETE without WHERE clause | CRITICAL | BLOCK |
VERICTO-002 | DELETE with LIMIT 0 (MySQL, SQLite) | 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-085 | Read of a sensitive column | HIGH | PER TAG |
VERICTO-086 | SQL text that MySQL and the engine would read differently (MySQL) | CRITICAL | BLOCK |
VERICTO-087 | Access outside the agent's allowlist | HIGH | BLOCK (ENFORCE) / FLAG (OBSERVE) |
VERICTO-090 | SQL injection: OR tautology in WHERE (OR 1=1) | CRITICAL | BLOCK |
VERICTO-091 | Stage unload or file transfer (Snowflake) | CRITICAL | BLOCK |
VERICTO-092 | External stage or integration (Snowflake) | CRITICAL | BLOCK |
VERICTO-093 | Share to another account (Snowflake) | CRITICAL | BLOCK |
VERICTO-094 | Recovery reduced (Snowflake) | HIGH | BLOCK |
VERICTO-095 | Warehouse cost (Snowflake) | HIGH | BLOCK |
VERICTO-096 | Code that runs later (Snowflake) | HIGH | BLOCK |
VERICTO-097 | Role switch (Snowflake) | HIGH | BLOCK |
VERICTO-098 | ATTACH DATABASE (SQLite) | HIGH | BLOCK |
VERICTO-099 | Safety PRAGMA (SQLite) | HIGH | BLOCK |
VERICTO-100 | Extension loading or file functions (SQLite) | CRITICAL | BLOCK |
VERICTO-101 | VACUUM INTO (SQLite) | HIGH | BLOCK |