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

SAFE

Read-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

HIGH

Writes and schema changes that may modify database state but are not classified as table-destructive.

Examples:

  • INSERT INTO users (name, email) VALUES (...) — data write
  • UPDATE users SET name = 'Ana' WHERE id = 7 — scoped update
  • DELETE FROM sessions WHERE expired = true — scoped deletion
  • ALTER TABLE users ADD COLUMN metadata jsonb — DDL, non-destructive
  • CREATE INDEX CONCURRENTLY ... — resource-intensive DDL

Default action: Require approval.


IMPACT_CRITICAL

IMPACT_CRITICAL

Operations 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

CRITICAL

Operations that are irreversible without a snapshot. These destroy data permanently at the table level.

Examples:

  • DROP TABLE users — destroys table and all data
  • TRUNCATE orders — removes all rows, cannot be rolled back
  • DELETE FROM payments — unscoped delete of entire table
  • ALTER 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.