Custom Rules.
Define your own deterministic AST conditions to block domain-specific queries that standard rules do not cover.
What are custom rules?
Vericto's standard rules cover the most common destructive patterns (DELETE without WHERE, DROP TABLE, TRUNCATE, etc.). Custom rules let you extend that ruleset with conditions specific to your business, defined through a YAML condition over the query's AST syntax tree.
Like all Vericto rules, custom rules are deterministic: the same input always produces the same result. There are no ML models, no variance. A query either meets the condition or it doesn't, no ambiguity.
Common use cases:
- Block
SELECT *on payment or user tables (prevents mass data exfiltration) - Prevent
UPDATEwithout a WHERE clause on high-criticality tables - Detect calls to dangerous functions like
SLEEP()orPG_SLEEP() - Block queries that access internal schemas (
information_schema,pg_catalog) - Enforce index usage on specific columns (disallow queries without a predicate)
Availability by plan
Custom rules are available starting with the BUILDER plan ($49/mo).
| Plan | Custom rules | Limit |
|---|---|---|
| Free | Not available | — |
| Builder ($49/mes) | ✅ Included | Up to 50 custom rules |
| Team ($149/mes) | ✅ Included | Unlimited |
If you try to create a custom rule on the Free plan, the API responds with 403 Forbidden (code PLAN_UPGRADE_REQUIRED) and an upgrade link.
YAML schema
Each custom rule carries a YAML condition that defines when Vericto should block a query. The YAML must have at least the rule and node_type fields.
rule: nombre-interno-de-la-regla # identificador libre
node_type: DeleteStmt # tipo de nodo AST a capturar
condition:
where_clause: null # solo si falta WHERE
YAML fields
Internal identifier of the rule. Appears in logs and the audit trail. Use kebab-case, e.g.: block-select-star-payments.
AST node type that triggers the condition. See available node types.
Additional predicates on the node. If omitted, the rule fires for all nodes of that type.
Custom message included in the block response and the audit trail.
AST node types
AST nodes represent the type of SQL statement or expression that Vericto detects in the syntax tree. The dialect determines which parser is used (pg_query for PostgreSQL, sqlparser-rs for MySQL).
| node_type | Description | Dialect |
|---|---|---|
DeleteStmt | DELETE statement | postgres / all |
UpdateStmt | UPDATE statement | postgres / all |
SelectStmt | SELECT statement | postgres / all |
InsertStmt | INSERT statement | postgres / all |
DropStmt | DROP statement (TABLE, DATABASE, INDEX, SCHEMA) | postgres / all |
TruncateStmt | TRUNCATE statement | postgres |
AlterTableStmt | ALTER TABLE (DROP COLUMN, RENAME, etc.) | postgres |
FuncCall | SQL function call (SLEEP, PG_SLEEP, etc.) | postgres / all |
Available conditions
Inside condition: you can use the following predicates depending on the node type:
For any node
| Predicate | Value | Meaning |
|---|---|---|
relation: "nombre" | string | Scopes the rule to a specific table (case-insensitive; schema is ignored, so public.payments matches payments) |
For DELETE / UPDATE / SELECT
| Predicate | Value | Meaning |
|---|---|---|
where_clause: null | null | The query has NO WHERE clause |
where_always_true: true | true | Trivially true WHERE (1=1, true) |
For SELECT
| Predicate | Value | Meaning |
|---|---|---|
target_list: "*" | "*" | The query uses SELECT * |
has_limit: false | false | The query does not include LIMIT |
For FuncCall
| Predicate | Value | Meaning |
|---|---|---|
func_name: "nombre" | string | Name of the function to block (case-insensitive) |
For DropStmt
| Predicate | Value | Meaning |
|---|---|---|
object_type: "table" | "table", "database", "schema", "index" | Type of object being dropped |
For AlterTableStmt
| Predicate | Value | Meaning |
|---|---|---|
alter_kind: "drop_column" | "drop_column", "rename", "drop_constraint", "alter_column_type", "disable_trigger" | ALTER TABLE command subtype. Without this predicate, the rule fires for any ALTER TABLE. |
Practical examples
1. Block SELECT * (mass exfiltration)
rule: block-select-star
node_type: SelectStmt
condition:
target_list: "*"
Blocks queries like SELECT * FROM users or SELECT * FROM payments. Allows queries with explicit columns like SELECT id, email FROM users.
2. Block UPDATE without WHERE
rule: block-update-no-where
node_type: UpdateStmt
condition:
where_clause: null
Blocks UPDATE users SET status = 'blocked' but allows UPDATE users SET status = 'blocked' WHERE id = $1.
3. Detect SQL injection tautologies
rule: block-or-tautology
node_type: DeleteStmt
condition:
where_always_true: true
Blocks queries like DELETE FROM users WHERE id = 1 OR 1=1 by detecting the trivially true OR branch in the AST.
4. Block SLEEP calls (DoS)
rule: block-sleep-calls
node_type: FuncCall
condition:
func_name: sleep
Blocks SELECT SLEEP(10) (MySQL) and SELECT PG_SLEEP(10) (PostgreSQL) to prevent Denial of Service attacks.
5. Block SELECT without LIMIT
rule: require-limit-on-select
node_type: SelectStmt
condition:
has_limit: false
message: "Las queries SELECT deben incluir cláusula LIMIT para evitar scans completos de tabla"
Useful for very large tables where a full scan without a limit can significantly degrade performance.
6. Block DROP of any object
rule: block-all-drops
node_type: DropStmt
Without condition:, the rule triggers for any DROP (TABLE, INDEX, SCHEMA). Useful in production environments where no DROP should run from the application.
7. Protect a specific table
rule: protect-payments
node_type: DeleteStmt
condition:
relation: payments
where_clause: null
With relation you scope the rule to a single table: it blocks DELETE FROM payments without WHERE, but leaves other tables untouched. Combine any predicate with relation to limit its scope.
8. Block DROP COLUMN
rule: block-drop-column
node_type: AlterTableStmt
condition:
alter_kind: drop_column
Blocks ALTER TABLE users DROP COLUMN email (irreversible data loss) but allows other changes like RENAME or ADD COLUMN.
Preview before activating
Before saving a custom rule, the dashboard offers a preview that evaluates the YAML against the workspace's historical queries from the last 7 days.
The preview shows:
- Whether the YAML is valid (
validation_result.valid: true/false) - How many historical queries this rule would have blocked
- An example query that triggers the condition
- List of matching audit trail events
The preview is read-only: it doesn't block anything. The rule only starts blocking queries in real time when you click Create rule.
REST API for custom rules
Create a custom rule
POST /api/v1/workspaces/:workspace_id/rules/custom
Authorization: Bearer <token>
{
"name": "Bloquear SELECT * en pagos",
"severity": "warning",
"dialect": "postgres",
"ast_condition_yaml": "rule: block-select-star\nnode_type: SelectStmt\ncondition:\n target_list: \"*\""
}
// Response 201
{
"rule_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"code": "CUSTOM-001",
"name": "Bloquear SELECT * en pagos",
"severity": "warning",
"is_active": true,
"created_at": "2025-06-20T10:00:00Z",
"validation_result": { "valid": true, "errors": [] }
}
Preview before saving
POST /api/v1/workspaces/:workspace_id/rules/custom/preview
Authorization: Bearer <token>
{
"ast_condition_yaml": "rule: block-select-star\nnode_type: SelectStmt\ncondition:\n target_list: \"*\"",
"dialect": "postgres",
"lookback_days": 7
}
// Response 200
{
"matching_queries": [...],
"total_matches": 3,
"validation_result": { "valid": true, "errors": [] },
"example_triggering_query": "SELECT * FROM users"
}
Required fields
| Field | Type | Description |
|---|---|---|
name | string (1–128 chars) | Descriptive name of the rule |
severity | "critical" / "warning" / "info" | Severity: determines whether it triggers alerts |
dialect | "postgres" / "mysql" / "all" | SQL dialect the rule applies to |
ast_condition_yaml | string YAML (10–16384 chars) | AST condition that defines when to block |
Current limitations
- YAML conditions only support the predicates documented above: you can't write arbitrary expressions over the AST yet.
- The
condition.table_namefield (filter by a specific table name) is on the roadmap but not available yet. - Maximum of 50 custom rules on the Builder plan, unlimited on the Team plan.
- The YAML is validated on the server: a syntactically incorrect YAML returns
400 Bad Requestwith the validation errors. - Custom rules don't support data-modifying CTEs yet (e.g.:
WITH x AS (DELETE ...)). Standard rules VERICTO-001 through VERICTO-020 do detect them.
Always use the preview before activating a custom rule in production. A misconfigured rule can block legitimate queries. The Vericto team recommends activating in staging first and validating for 24h before moving to production.
Do you have a use case the current conditions don't cover? Write to us. Real customer cases guide the roadmap for new predicates.