El problema con los allowlists
Cuando los equipos de ingeniería piensan en proteger sus bases de datos de agentes LLM, la primera solución que aparece es un allowlist: define qué operaciones están permitidas (SELECT) y bloquea el resto (DELETE, DROP, UPDATE).
El problema es que este enfoque bloquea por el primer token de la query, no por su semántica. Y los agentes de IA generan patrones que un allowlist simple no puede detectar.
Un allowlist no sabe la diferencia entre
UPDATE users SET name = $1 WHERE id = $2yUPDATE users SET role = 'admin'. Para un allowlist, ambas son "UPDATE": o ambas pasan, o ambas se bloquean.
Si bloqueas todos los UPDATE, rompes tu aplicación. Si permites todos los UPDATE, estás a un prompt ambiguo de un desastre de producción.
Lo que un allowlist no puede detectar
Estos son cuatro patrones que pasan un allowlist de operaciones sin problema:
1. UPDATE sin WHERE
-- Un agente responde a "actualiza el precio del producto"
-- con contexto ambiguo y genera esto:
UPDATE products SET price = 0;
Un allowlist que permite UPDATE no detecta esto. La query modifica todas las filas de la tabla: en una tabla de catálogo con 50,000 productos, el daño es inmediato y, sin un backup, irreversible.
2. DELETE con WHERE siempre verdadero
-- El LLM fue instruido para "limpiar registros viejos" pero
-- generó una condición trivialmente verdadera:
DELETE FROM sessions WHERE 1=1;
Tiene WHERE: pasa cualquier filtro que solo verifique la presencia de WHERE. Pero WHERE 1=1 es semánticamente idéntico a no tener WHERE. El engine reconoce los predicados cuyos operandos son todos literales, como 1=1, que no pueden filtrar ninguna fila, y los trata como ausentes (VERICTO-003).
3. TRUNCATE escondido detrás de otra sentencia
-- Un agente que encadena pasos en un solo string:
SELECT count(*) FROM orders; TRUNCATE TABLE orders;
El primer token es SELECT. Un allowlist que solo mira el inicio del string lo deja pasar. El parser separa cada sentencia del string y evalúa todas: encuentra el TruncateStmt y bloquea la query (VERICTO-011).
4. DROP TABLE dentro de un bloque DO
-- El DROP queda dentro de un bloque PL/pgSQL:
DO $$
BEGIN
DROP TABLE IF EXISTS users;
END $$;
El primer token es DO, no DROP. Un allowlist que solo verifica tokens iniciales lo deja pasar. El cuerpo de un bloque DO es PL/pgSQL, opaco para el parser de SQL, así que Vericto no intenta adivinar qué hay dentro: bloquea todo bloque DO con VERICTO-081, porque puede esconder cualquier DML o DDL.
Cómo funciona el AST parsing frente a estos casos
El AST (Abstract Syntax Tree) parsing construye una representación completa del árbol sintáctico de la query. En lugar de leer el texto de izquierda a derecha, el parser convierte la query en una estructura de datos jerárquica donde cada nodo tiene un tipo, propiedades y nodos hijo.
Para la query UPDATE products SET price = 0, el árbol AST se ve así:
UpdateStmt
├── relation: "products"
├── targetList:
│ └── ResTarget
│ ├── name: "price"
│ └── val: Integer(0)
└── whereClause: NULL ← el nodo que activa VERICTO-042
La regla VERICTO-042 evalúa una condición: que el UpdateStmt no tenga un WHERE efectivo (ausente o siempre verdadero). Si se cumple, la query se bloquea, sin importar qué tabla, qué columnas o qué valores modifique.
Para el caso de WHERE 1=1, la evaluación va un paso más allá:
DeleteStmt
├── relation: "sessions"
└── whereClause:
└── A_Expr
├── kind: AEXPR_OP
├── name: "="
├── lexpr: Integer(1)
└── rexpr: Integer(1) ← ambos operandos son literales iguales
→ evaluable estáticamente → siempre true
El engine de Vericto reconoce los predicados que no dependen de ninguna fila porque todos sus operandos son literales: 1=1, 'a'='a', WHERE TRUE, NOT FALSE, 1 IN (1, 2). Los trata como ausencia de WHERE. Un predicado que referencia una columna, como id IS NOT NULL, sí filtra (excluye los NULL), así que no se reporta.
¿No es suficiente con los permisos de base de datos?
Es la objeción más frecuente: "simplemente le damos al agente un usuario de base de datos con permisos solo de SELECT".
El problema con esta estrategia es que es demasiado restrictiva en la práctica. Los agentes de IA que construyen valor real necesitan poder escribir: insertar eventos, actualizar estados, registrar métricas. Un usuario solo-SELECT no puede hacer nada de esto.
Y cuando le das permisos de escritura para las operaciones legítimas, estás de vuelta al problema original: el agente puede generar un UPDATE o DELETE sin WHERE clause, y los permisos de base de datos no distinguen entre UPDATE users SET name = $1 WHERE id = $2 y UPDATE users SET role = 'admin'.
Los permisos de base de datos y el AST parsing son capas de defensa complementarias, no alternativas. Vericto no reemplaza el principio de least privilege en tu base de datos: lo complementa con un análisis semántico que los permisos no pueden hacer. Y si quieres limitar qué tablas y columnas toca cada agente, las allowlists de acceso por agente de Vericto (VERICTO-087) también trabajan sobre el AST: cuentan cada referencia de la query, no solo el primer token.
Por qué importa que sea determinístico
La alternativa al AST parsing para detectar queries peligrosas es usar otro modelo de ML que "entienda" si una query es peligrosa. Este enfoque tiene tres problemas fundamentales que lo hacen inadecuado para el path de seguridad:
- Falsos negativos: un modelo entrenado en queries históricas puede no conocer los patrones específicos que genera tu LLM en tu dominio
- Falsos positivos: bloquear queries legítimas en producción es tan catastrófico para la experiencia del equipo como dejar pasar queries destructivas
- No auditable: no puedes incluir en un reporte SOC2 "un modelo con 99.2% de accuracy protege nuestros datos", pero sí puedes incluir "el parser AST bloquea determinísticamente cualquier UpdateStmt con whereClause = NULL"
Determinismo no es solo una propiedad técnica: es un requisito para que el control sea auditable en revisiones SOC 2 o ISO 27001. La misma query con el mismo ruleset produce el mismo veredicto en cada ejecución, con cualquier carga del sistema. No hay varianza estocástica, ni drift de modelo, ni temperatura de inferencia.
Cómo implementarlo en <5 minutos
Si tienes un pipeline LLM-to-SQL sobre Postgres o MySQL, la integración de Vericto es un cambio de una línea en tu connection string:
- DATABASE_URL=postgres://user:pass@prod-db.host:5432/mydb
+ DATABASE_URL=postgres://user:pass@localhost:5433/mydb
Tu ORM (Prisma, SQLAlchemy, Drizzle, ActiveRecord) no sabe que hay un proxy en el medio. Vericto intercepta cada query, analiza el AST y, si es segura, la reenvía a tu base de datos real; si es destructiva, la bloquea con un error nativo del protocolo. El objetivo de diseño es que el parsing y la evaluación de reglas tomen menos de 2 ms en el p99.
Puedes explorar las 31 reglas del catálogo y sus condiciones AST en la Referencia de Reglas AST.