-
Notifications
You must be signed in to change notification settings - Fork 0
ARGUS A13
Rule Code:
ARGUS-A13Identifier:MISSING_DOWN_MIGRATIONSeverity:HIGH / CRITICAL(Failed Automated Rollback & Half-Migrated State Blocker) Category:Schema Evolution, Incident Recovery & Deployment SafetyTarget Standards: CWE-1033 (Incomplete Component Recovery), Operational Zero-Downtime Standards, OWASP ASVS v4.0.3/v5.0 §V1.4.3
Every forward database schema migration file (.up.sql) must have a corresponding, non-empty, and executable reverse rollback migration file (.down.sql).
Automated deployment orchestrators and CI/CD pipelines require instantaneous rollback capabilities if application health checks fail during release deployment. Rollback scripts must never be omitted, left as 0-byte placeholders, or contain only whitespace and comments.
PostgreSQL supports transactional DDL (BEGIN ... DDL ... COMMIT), allowing multi-statement migrations to roll back if a syntax or constraint error occurs during execution. However, once a migration commits successfully and the subsequent application startup fails (e.g. environment variable misconfiguration or startup panic), transactional DDL cannot revert committed catalog modifications. Reversion requires executing the corresponding .down.sql script.
When deployment automation triggers a rollback to the previous release:
-
Unpaired Migration: Migration
000025_add_documents.up.sqlwas applied to the database. - Application Startup Failure: The new application version fails post-deployment health checks.
-
Rollback Attempt: CI/CD initiates an automated rollback to version
000024, looking for000025_add_documents.down.sql. -
Catastrophic Outage: Because the
.down.sqlfile is missing or empty, the automated rollback fails. The database remains stuck in a half-migrated state where old application pods fail to run against the new schema, triggering extended downtime.
flowchart TD
subgraph OUTAGE ["Missing Down Migration Disaster (CWE-1033)"]
direction TB
UpApply["1. CI/CD Applies: 000025_add_docs.up.sql (COMMITTED)"] --> AppFail["2. New App Pod Crashes on Startup"]
AppFail --> Rollback["3. CI/CD Triggers Auto-Rollback to V24"]
Rollback --> Missing["4. 000025_add_docs.down.sql NOT FOUND / EMPTY!"]
Missing --> HalfMigrated["5. Database Stuck in Half-Migrated State<br/>Old Pods Cannot Serve V25 Schema -> OUTAGE!"]
end
subgraph RECOVERY ["Deterministic Symmetric Rollback (COMPLIANT)"]
direction TB
U["1. CI/CD Applies: 000025_add_docs.up.sql"] --> F["2. New App Pod Fails Health Check"]
F --> R["3. CI/CD Executes: 000025_add_docs.down.sql"]
R --> Clean["4. Schema Cleanly Reverted to V24"]
Clean --> OldPods["5. Old Pods Continue Serving Traffic Seamlessly"]
end
For historical migrations involving lossy or mathematically irreversible data transformations:
- A physical
.down.sqlfile is still mandatory. - The file must contain a no-op statement (
SELECT 1;) accompanied by an approved ADR reference and suppression directive:-- argus:ignore ARGUS-A13 ADR-0042 irreversible historical data migration SELECT 1;
Argus evaluates migration directory pairings and AST statement content:
flowchart LR
Scan["Scan Migration Directory<br/>(db/migrations)"] --> PairCheck{"For Each .up.sql:<br/>Does .down.sql Exist?"}
PairCheck -->|No| ReportMissing["Report CRITICAL Violation:<br/>Missing .down.sql File"]
PairCheck -->|Yes| ReadDown["symmetry_ast.go:<br/>Parse .down.sql AST"]
ReadDown --> EmptyCheck{"Is 0 Bytes or<br/>No Executable SQL?"}
EmptyCheck -->|Yes| TagCheck{"Has Valid ADR<br/>Ignore Directive?"}
TagCheck -->|No| ReportEmpty["Report HIGH Violation:<br/>Empty / Invalid .down.sql"]
TagCheck -->|Yes| Pass["Pass (Verified ADR Exemption)"]
EmptyCheck -->|No| Pass["Pass (Valid Symmetric Pair)"]
-
Pairing Matcher (
pair_matcher.go): Validates 1-to-1 filesystem mapping betweenNNNN_name.up.sqlandNNNN_name.down.sql. -
AST Statement Validator (
symmetry_ast.go): Ensures.down.sqlcontains executable SQL statements usingpg_query_go. -
Standalone Runner (
standalone_scanner.go): Independent directory auditor capable of running in CI/CD pre-commit hooks.
| Failure Mode | Technical Impact | Risk Severity |
|---|---|---|
Missing .down.sql File |
Prevents automated rollback during failed deployments, leaving cluster in half-migrated state. | CRITICAL |
0-Byte Empty .down.sql |
Bypasses superficial file existence checks without providing rollback capability. | HIGH |
Comments-Only .down.sql |
Contains no executable SQL statements to undo schema changes. | HIGH |
db/migrations/
├── 000001_init.up.sql
├── 000001_init.down.sql
├── 000002_add_orders.up.sql <-- Missing 000002_add_orders.down.sql!
-- 000003_add_coupons.down.sql
-- (File is empty: 0 bytes)-- 000004_drop_temp_index.down.sql
-- TODO: Add rollback later when time permits-- 000001_create_accounts.up.sql
CREATE TABLE accounts (
id UUID PRIMARY KEY,
balance BIGINT NOT NULL DEFAULT 0
);
-- 000001_create_accounts.down.sql
DROP TABLE IF EXISTS accounts;-- 000002_add_account_status.up.sql
ALTER TABLE accounts ADD COLUMN status VARCHAR(32) NOT NULL DEFAULT 'active';
-- 000002_add_account_status.down.sql
ALTER TABLE accounts DROP COLUMN IF EXISTS status;-- 000003_backfill_payout_hash.down.sql
-- argus:ignore-a13 ADR-0089 hash backfill is computationally irreversible
SELECT 1;For approved irreversible data backfills documented in Architecture Decision Records:
-- argus:ignore-a13 ADR-0042 lossy data transformation irreversible
SELECT 1;Alternatively, use the canonical identifier alias:
-- argus:ignore MISSING_DOWN_MIGRATION ADR-0042 irreversible data migration
SELECT 1;Enable or configure this rule in .argus.yaml:
rules:
ARGUS-A13:
enabled: true