SQLite.
Cómo proteger una base SQLite con Vericto. SQLite no tiene servidor, así que no hay proxy: la evaluación ocurre en tu código, con la API de runtime, la CLI o MCP (los SDK de Python y Node, próximamente).
SDK: próximamente. El dialecto SQLite y las reglas VERICTO-098 a VERICTO-101 están disponibles en la API de runtime, la API de validación, la CLI y MCP. Los SDK de Python (vericto) y Node (@vericto/sdk) todavía no están publicados en PyPI ni en npm: las secciones de Python y Node describen cómo funcionarán.
Resumen
SQLite es un dialecto más de Vericto (vericto-engine 4.1.0): el mismo motor determinístico parsea cada sentencia con el dialecto sqlite y la evalúa contra las reglas de tu workspace, las columnas sensibles y las allowlists de agentes. Registra la base con el dialecto SQLite en Dashboard → Bases de datos y protégela desde cualquiera de estos caminos:
- API de runtime: tu código envía cada query a
POST /api/v1/proxy/querycon"dialect": "sqlite"y la ejecuta solo si Vericto la deja pasar. - CLI en CI/CD:
vericto check --dialect sqlitesobre tus migraciones. - MCP: tu agente de código llama a
check_sqlcondialect: "sqlite"antes de ejecutar SQL en una base local. - SDK de Python o Node (próximamente): un wrapper de
sqlite3o debetter-sqlite3que evaluará cada sentencia antes de que el driver la ejecute.
Por qué no hay proxy TCP
SQLite no es un servidor: es una biblioteca dentro del proceso de tu aplicación, y cada query es una llamada a una función de C sobre un archivo. No hay socket ni protocolo de red en el que pueda ponerse un proxy. Por eso una base SQLite se registra solo en modo HTTP, y el dashboard no ofrece datos de conexión de proxy para ella.
El punto de control es tu código. La llamada a la API de runtime (o, cuando se publiquen, el wrapper del SDK) es lo que aplica la decisión. El SQL que se ejecuta por otra conexión al mismo archivo, sin pasar por ese control, no se evalúa. Como segunda capa, el autorizador de SQLite (sqlite3_set_authorizer, Connection.set_authorizer en Python) puede denegar ATTACH y PRAGMA al preparar cada sentencia.
Python: vericto.sqlite (próximamente)
Próximamente. El SDK de Python todavía no está publicado en PyPI; esta sección describe cómo funcionará. Usará una API key con el scope proxy:evaluate y la base registrada. vericto.sqlite.connect() devolverá una sqlite3.Connection real (pandas y otras bibliotecas seguirán funcionando) cuyos execute, executemany y executescript se evaluarán antes de ejecutarse; executescript evaluará cada sentencia del script antes de correrlo. Mientras tanto, usa la API de runtime.
export VERICTO_API_KEY=vtro_... # scope proxy:evaluate
export VERICTO_DATABASE_ID=<database-id> # the SQLite database in the dashboard
import vericto.sqlite
from vericto import VerictoBlocked
conn = vericto.sqlite.connect("app.db")
conn.execute("DELETE FROM sessions WHERE expires_at < ?", (now,)) # allowed: it runs
try:
conn.execute("DELETE FROM sessions") # no WHERE: blocked, nothing runs
except VerictoBlocked as exc:
print("blocked:", exc)
Node.js: @vericto/sdk/better-sqlite3 (próximamente)
Próximamente. El SDK de Node todavía no está publicado en npm; esta sección describe cómo funcionará. guardDatabase() envolverá una base de better-sqlite3. Los puntos de entrada que llevan SQL (prepare, exec y los atajos run/all/get) pasarán a ser asíncronos; una sentencia preparada se evaluará una vez y sus run/all/get seguirán siendo síncronos. Mientras tanto, usa la API de runtime.
import Database from 'better-sqlite3';
import { guardDatabase } from '@vericto/sdk/better-sqlite3';
const db = await guardDatabase(new Database('app.db'));
const expire = await db.prepare('DELETE FROM sessions WHERE expires_at < ?'); // checked once
expire.run(Date.now());
await db.exec(migrationSql); // every statement of the script is checked first
Los dos SDK fallarán cerrado por defecto (VERICTO_FAIL_MODE=closed): si Vericto no responde, la sentencia no se ejecuta. Una respuesta con rewritten_query (una columna enmascarada) se ejecutará en lugar de la original.
API de runtime
Desde cualquier otro lenguaje o driver (Go database/sql, Rust rusqlite, Cloudflare D1 desde un Worker), envía cada query antes de ejecutarla. 200: ejecútala (o ejecuta rewritten_query si viene); 403: no la ejecutes. El contrato completo está en la Guía de integración.
curl -X POST https://api.vericto.com/api/v1/proxy/query \
-H "Authorization: Bearer $VERICTO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "ATTACH DATABASE '\''/tmp/x.db'\'' AS x", "database_id": "<database-id>", "dialect": "sqlite"}'
# 403 → {"decision": "BLOCKED", "rule_code": "VERICTO-098", "severity": "high", ...}
CLI en CI/CD
Valida las migraciones de SQLite (Drizzle, Prisma, Django, Rails, Alembic, Flyway) antes del merge. El SQL se analiza, nunca se ejecuta. Guarda default_dialect = "sqlite" en .vericto.toml para no repetir la opción.
VERICTO_API_KEY=vtro_... vericto check migrations/*.sql --dialect sqlite
Las migraciones de SQLite reconstruyen tablas con PRAGMA foreign_keys = OFF. Eso lo reporta VERICTO-099, que es de clase SchemaMigration: el límite de migraciones de esquema del canal de CI lo baja a FLAGGED, igual que un DROP TABLE, mientras que en runtime se bloquea.
Agentes de IA (MCP)
Un agente de código que trabaja sobre una base SQLite local llama a check_sql con dialect: "sqlite" antes de ejecutar el SQL con su propia herramienta. whoami lista las bases del workspace con su dialecto. Configuración y cuota: Agentes de IA (MCP).
Reglas de SQLite
Cuatro reglas que solo se disparan en SQLite (el detalle está en la referencia de reglas):
VERICTO-098ATTACH DATABASE (HIGH):ATTACHabre, o crea, otro archivo de base de datos en la conexión: acceso arbitrario a archivos y lecturas entre bases.DETACHno se reporta.VERICTO-099PRAGMA que desactiva una protección (HIGH):writable_schema,trusted_schema = ON,foreign_keys = OFF,ignore_check_constraints = ON,journal_mode = OFF,synchronous = OFF,query_only = OFF,schema_version, entre otros. Las lecturas (PRAGMA table_info(t)) y los ajustes seguros (journal_mode = WAL,busy_timeout) no se reportan.VERICTO-100Carga de extensiones o funciones de archivos (CRITICAL):load_extension()(código nativo),fts3_tokenizer()yreadfile(),writefile(),edit(),fsdir()yzipfile()del shell de sqlite3, en cualquier parte de la sentencia.VERICTO-101VACUUM INTO (HIGH): una copia completa de la base escrita en una ruta. UnVACUUMsimple no se reporta.
De las reglas estándar, 15 se aplican tal cual, 5 tienen una variante para SQLite (por ejemplo, WHERE '1abc' es verdadero en SQLite) y 11 no se aplican porque SQLite no tiene esa construcción (TRUNCATE, GRANT, MERGE…). La lista está en la referencia de reglas.
Columnas sensibles y máscaras
Las columnas sensibles funcionan en SQLite. Una etiqueta mask reescribe la proyección con funciones del núcleo de SQLite: full, last4 y email. Con la API de runtime se ejecuta rewritten_query (los SDK, cuando se publiquen, harán lo mismo). Algunas máscaras bloquean la query en lugar de reescribirla:
hashbloquea en SQLite. El núcleo de SQLite no tiene SHA-256 (necesita una extensión), así que la máscara no se puede aplicar y la query se bloquea. Usafull,last4oemail, o la políticablock.- Un nombre entre comillas dobles bloquea la máscara. SQLite lee
"x"como un string si no existe una columnax(el modo DQS), así que reescribir esa query podría cambiar un literal. Con máscaras, escribe los nombres sin comillas o con corchetes, y desactiva DQS en tu conexión (SQLITE_DBCONFIG_DQS_DML=0). Las políticasblockyflagcuentan"x"como una columna, que es la lectura prudente. ORDER BY,GROUP BYoHAVINGsobre una columna enmascarada, o una expresión sin alias sobre ella, bloquean la query: SQLite resolvería esos nombres al valor enmascarado.
Acceso de agentes
En las allowlists de agentes, los nombres de SQLite (esquema, tabla y columna) se comparan sin distinguir mayúsculas, como los compara SQLite, y se aceptan "x", `x` y [x]. Una entrada sin esquema es la tabla de main; temp y las bases adjuntas son otros esquemas. sqlite_master, sqlite_schema y las funciones pragma_* necesitan una entrada que las nombre. Con una allowlist se deniegan ATTACH, DETACH, VACUUM, REINDEX, ANALYZE y los PRAGMA, salvo las lecturas de estructura de una tabla permitida (PRAGMA table_info(t)) y los ajustes de conexión que envían los drivers y ORMs (foreign_keys = ON, busy_timeout, journal_mode = WAL…).
Errores de parseo
Una sentencia de SQLite que el parser no lee sigue la política de errores de parseo del workspace, como en cualquier dialecto: se marca por defecto y se bloquea con parse_error: block, con una columna etiquetada block o mask, o con una allowlist en enforce. Las sentencias de SQLite que el parser no lee pero el motor conoce se reconocen igual: las peligrosas (VACUUM INTO, PRAGMA … = OFF) como hallazgos de su regla, y las inofensivas (PRAGMA table_info(t), DETACH, REINDEX) sin hallazgo.
Cada base de datos puede reemplazar la política del workspace con la suya (SQL no parseable por base de datos): Heredar, Marcar o Bloquear. Recomendado: Bloquear si a la base acceden agentes de IA.