Risk classification
How Backstop classifies routed SQL using AST parsing, impact analysis, and table-level heuristics.
Backstop classifies every SQL statement routed through the gateway before applying policy. Classification happens at the AST level — the parser extracts the operation type, target, scope, and an impact estimate.
Risk levels
SAFE
SAFERead-only and non-destructive operations. No data is modified, deleted, or altered.
Examples: SELECT, SHOW, SET, EXPLAIN, ANALYZE, VACUUM (non-destructive), EXPLAIN ANALYZE
Default action: Execute immediately, no approval required.
HIGH
HIGHWrites and schema changes that may modify database state but are not classified as table-destructive.
Examples:
INSERT INTO users (name, email) VALUES (...)— data writeUPDATE users SET name = 'Ana' WHERE id = 7— scoped updateDELETE FROM sessions WHERE expired = true— scoped deletionALTER TABLE users ADD COLUMN metadata jsonb— DDL, non-destructiveCREATE INDEX CONCURRENTLY ...— resource-intensive DDL
Default action: Require approval.
IMPACT_CRITICAL
IMPACT_CRITICALOperations where impact analysis estimates the affected row count or percentage exceeds your configured thresholds, even if the SQL itself starts as HIGH. Requires impact analysis to be enabled.
How it works: Backstop asks PostgreSQL for an EXPLAIN (FORMAT JSON) plan for the original write statement and compares the estimated affected rows against the configured thresholds. It also escalates writes touching configured protected tables or protected columns.
Example: A DELETE with a WHERE clause that still affects 80% of a large table.
Default action: Require approval + snapshot.
CRITICAL
CRITICALOperations that are irreversible without a snapshot. These destroy data permanently at the table level.
Examples:
DROP TABLE users— destroys table and all dataTRUNCATE orders— removes all rows, cannot be rolled backDELETE FROM payments— unscoped delete of entire tableALTER TABLE users DROP COLUMN email— destroys column data
Default action: Require a verified snapshot + operator approval before execution.
Blocked by default: DROP DATABASE, DROP SCHEMA — these cannot be snapshotted at table level.
Impact analysis
Impact analysis runs on UPDATE and DELETE when impact_analysis_enabled: true. It estimates:
- Estimated affected rows — using PostgreSQL's plan estimate for the original statement
- Affected percent — relative to the table's row count
- Protection check — whether the target table or columns are in
protected_tables/protected_columns
If impact exceeds configured thresholds, the risk level is escalated. This catches the case where a DELETE WITH WHERE is technically scoped but still catastrophically large.
{
"impact_analysis_enabled": true,
"max_write_rows_without_critical": 1000,
"max_write_percent_without_critical": 50
}Classification response fields
When Backstop classifies a query, the response includes a full safety_metadata object:
{
"risk_level": "CRITICAL",
"operation": "DROP",
"table": "users",
"schema": "public",
"table_recoverable": true,
"recovery_required": true,
"recovery_possible": true,
"policy_action": "approve",
"policy_reason": "Risk level CRITICAL requires approval and snapshot",
"requires_approval": true,
"parse_error_present": false,
"estimated_affected_rows": 1842933,
"affected_percent": 100.0,
"protected_table": false,
"protected_columns": []
}Parse failures
If a SQL statement cannot be parsed, it is classified as CRITICAL (when block_unknown_or_parse_failure: true) or logged with a warning. The parse_error_present field is set to true.
This prevents an attacker or misbehaving agent from bypassing classification by submitting malformed SQL that a regex-based classifier would miss.