Guardrails SQL determinísticos
Vericto no adivina. Parsea cada sentencia a su árbol sintáctico (AST), con pg_query para Postgres y sqlparser-rs para MySQL, Oracle, SQL Server, Snowflake y SQLite, y evalúa sobre ese árbol las reglas activas de tu workspace. Sin ML ni umbrales: el mismo SQL con las mismas reglas da siempre el mismo veredicto. El motor es código abierto (AGPL-3.0) y está en GitHub.
Qué bloquea y qué marca
| Riesgo | Reglas | Por defecto |
|---|---|---|
DELETE y UPDATE sin WHERE, con un WHERE siempre verdadero (1=1, OR 1=1) o con LIMIT 0 en MySQL y SQLite | VERICTO-001, 002, 003, 030, 042, 090 | block |
| DROP TABLE, DROP DATABASE y DROP SCHEMA | VERICTO-010, 012 | block |
| TRUNCATE | VERICTO-011 | block |
| DDL riesgoso: DROP INDEX sin IF EXISTS, DROP COLUMN, RENAME, DROP CONSTRAINT, ALTER COLUMN TYPE, DISABLE TRIGGER o ROW LEVEL SECURITY y DROP TRIGGER | VERICTO-013, 015–019 | block |
SELECT sin LIMIT y SELECT * sin WHERE | VERICTO-050, 051 | flag |
Cargas masivas: INSERT de más de 10.000 filas y COPY … FROM un archivo del servidor | VERICTO-061 | flag |
COPY … PROGRAM (un comando de shell en el servidor) y bloques anónimos DO | VERICTO-080, 081 | block |
| GRANT y REVOKE | VERICTO-082 | block |
| MERGE y CREATE TABLE AS SELECT | VERICTO-083, 084 | block |
La acción por defecto sale de la severidad: CRITICAL y HIGH bloquean (block), MEDIUM deja pasar la query y genera una alerta (flag), y LOW e INFORMATIONAL solo la registran (monitor). El owner o un admin del workspace puede cambiar la acción por severidad o empezar en modo observación. Desde el plan Builder puedes sumar reglas propias en YAML.
DROP TRIGGER (VERICTO-019) y COPY … FROM un archivo del servidor (VERICTO-061) se reportan desde la versión 3.9.0 del motor, que el proxy TCP incluye desde su versión 4.9.0. El evaluador de Vericto, que atiende la API, la CLI y MCP, los reporta cuando adopta esa versión del motor.
42 reglas · 6 dialectos · 8 familias de incidentes · 52 escenarios
Los escenarios van desde un DELETE sin WHERE escrito por un agente hasta un COPY … TO PROGRAM inyectado a través de un servidor MCP. Cada uno tiene su regla, su acción por defecto, sus dialectos y su origen en la referencia de reglas, con un filtro por familia.
Un DELETE escondido en un CTE, una tautología en una tabla derivada o en una rama UNION, un DROP apilado después de un COMMIT, un INSERT … SELECT dentro de un trigger de SQLite: el motor recorre el árbol sintáctico hasta 50 niveles y evalúa cada sentencia de un string multi-sentencia. Lo que es más profundo, o lo que el parser no puede leer, no pasa en silencio: queda registrado como error de parseo, y se bloquea si eliges parse_error: block.
Latencia medida, con sus condiciones
Medido en EC2 dedicado el 31 de octubre de 2026 (BENCH-2026-11, actualización para 4.21.0): vericto-proxy 4.21.0 (motor 4.6.0, 42 reglas estándar, política por defecto) en modo enforce con la telemetría completa, limitado a 0,5 vCPU (un núcleo Graviton3 con una cuota CFS del 50%, como una tarea de Fargate), TLS en los dos tramos, en una c7g.2xlarge con PostgreSQL 17.11 y MySQL 8.4.13, y la carga desde una c7g.xlarge en el mismo cluster placement group. Lazo abierto a tasas fijas, latencia desde la hora prevista de envío, 60 s de calentamiento y 300 s medidos por celda, 3 repeticiones intercaladas, cada una comparada con la ruta directa a la misma tasa y en la misma repetición; las curvas principales se repitieron en hosts nuevos.
- Agregado en p50 (proxy menos directo): PostgreSQL 0,08 ms de 967 a 4.834 sentencias/s (pgbench select-only, protocolo extendido; la ruta directa, de 0,30 a 0,32 ms); MySQL 1,1 ms por transacción sysbench oltp_read_only de 16 sentencias y unos 40 KB, unos 0,07 ms por sentencia, de 36 a 250 transacciones/s.
- Agregado en p99: PostgreSQL 0,13 ms al 10% de la capacidad de la tarea, 0,28 ms al 30% y 0,60 ms al 50%; MySQL 1,33 ms por transacción al 10% y de 1,54 a 2,01 ms del 30 al 70%. La cola crece con la carga sobre la cuota de 0,5 vCPU (las esperas de la cuota CFS, no la evaluación): dimensiona la tarea para que trabaje cerca del 50% de su CPU. Un salto solo TCP (HAProxy) con el mismo presupuesto de CPU agregó de 0,03 a 0,04 ms en p50 en PostgreSQL.
- Tamaño de la sentencia (contexto de staging, no de esta corrida): lo agregado crece con el tamaño. En staging el 16 de octubre de 2026 (vericto-proxy 4.12.0, 0,25 vCPU), un INSERT de 10 filas agregó 0,58 ms en p50 en PostgreSQL; de 100 filas, 1,05 ms, ya por encima del milisegundo; de 500, 3,07 ms; de 1.000, 5,54 ms. El barrido por tamaño en EC2 está en la siguiente tanda.
- Solo la evaluación de reglas, sin red (staging, 16 de octubre de 2026): p99 de 100 µs o menos para las sentencias OLTP (vericto-engine 3.9.0 en 0,25 vCPU ARM64).
Es una medición en esas condiciones, no una garantía: tu red (aquí el tramo del proxy a la base está en el mismo host), el tamaño de la CPU, tus sentencias y el tamaño de los resultados cambian el resultado. El modo observe, las columnas enmascaradas, 1 vCPU y las sentencias grandes no formaron parte de esta corrida. Método completo, gráficos y límites: benchmark de latencia del proxy.
Así se ve un veredicto
Tres sentencias de un script, evaluadas con check_sql. Cada hallazgo trae la línea, el estado, la regla, la severidad y la corrección sugerida:
BLOCKED: 3 of 3 statements must not ship as written.
- line 1 BLOCKED VERICTO-001 (critical): DELETE FROM orders Fix: DELETE FROM orders WHERE id = $1
- line 2 BLOCKED VERICTO-010 (critical): DROP TABLE orders Fix: Use a versioned migration tool (Flyway, Prisma Migrate, Alembic, Rails migrations)
- line 3 BLOCKED VERICTO-080 (critical): COPY orders FROM PROGRAM 'curl -s https://example.com/x.csv' Fix: Use client-side \copy or COPY … TO STDOUT, never PROGRAM
El DROP TABLE sale BLOCKED: check_sql es la consulta de un agente antes de ejecutar, y bloquea como en runtime. Con la CLI o en CI, la misma sentencia sale FLAGGED, porque ahí una migración la revisan personas.

Dónde se aplica
- Proxy TCP para Postgres y MySQL: se despliega en tu infraestructura y la query bloqueada no llega a la base de datos. La aplicación recibe el error nativo de su driver, SQLSTATE 42501 en Postgres y ERROR 1142 en MySQL, con la regla.
- API de runtime: tu aplicación envía cada query antes de ejecutarla y la ejecuta solo si la respuesta es
200. Sirve también para Oracle, SQL Server, Snowflake y SQLite. - CLI y CI:
vericto checken pre-commit y en el pipeline, o la GitHub Action, que anota los hallazgos en el PR y falla el job ante un bloqueo. - MCP: tu agente de código llama a
check_sqlmientras escribe la migración o la query. Es el mismo motor, así que funciona como guardrail de base de datos para agentes de IA antes de que exista un commit.
- uses: vericto/check-sql@v1
with:
api-key: ${{ secrets.VERICTO_API_KEY }}
Dialectos: PostgreSQL, MySQL, Oracle, SQL Server, Snowflake y SQLite. El proxy TCP cubre Postgres y MySQL; la API de runtime, la CLI y MCP, los seis.
En runtime y en MCP bloquea; en CI, las migraciones informan
Una migración versionada contiene DROP, ALTER y TRUNCATE con toda razón. Por eso, en la CLI (vericto check), la API de validación y el dry-run del dashboard, las reglas de esquema y DDL (VERICTO-010 a 019) se limitan a flag (schema_migration_cap): el pipeline reporta el hallazgo y no falla, y la migración la revisan personas en el PR. El DML destructivo, como un DELETE sin WHERE, y las reglas de seguridad mantienen ahí la acción de tu workspace. En MCP check_sql, la API de runtime, los SDKs y el proxy TCP no hay tope: todas las reglas aplican su acción completa, así que un DROP TABLE de un agente se bloquea.
Límites
- Una sentencia que no se puede parsear se reenvía y queda registrada como
PARSE_ERROR(fail-open) por defecto, para no cortar el tráfico. El owner o un admin puede pasar el workspace a fail-closed. - En MySQL, el texto que MySQL y el motor podrían leer distinto, por sus comentarios ejecutables y sus escapes de string, siempre se bloquea (VERICTO-086). Esa regla no se desactiva ni baja de severidad.
- Vericto lee el SQL, no tu esquema ni tus datos: una tautología que depende de los valores de una columna, como
WHERE id = 1 OR id > 0, queda fuera de su alcance. - Es defensa en profundidad: los roles, los permisos y los backups de tu base de datos siguen siendo necesarios.
Las reglas estándar están en todos los planes, también en Free. Las reglas custom en YAML están desde el plan Builder.