-
Notifications
You must be signed in to change notification settings - Fork 0
ARGUS A30
will2469 edited this page Aug 29, 2026
·
1 revision
| Meta Field | Specification |
|---|---|
| Rule Code | ARGUS-A30 |
| Identifier | TIMESTAMP_WITHOUT_TIMEZONE |
| Severity | CRITICAL |
| Category | Database Schema Migration, Temporal Data Integrity & Audit Determinism |
| Analysis Layer | Layer 1 - Pure SQL-AST Migration Analysis |
| CWE Mapping | CWE-361: Time-of-check Time-of-use (TOCTOU) Race Condition, CWE-662: Improper Synchronization |
| OWASP ASVS | OWASP ASVS v4.0.3/v5.0 §V10.3.2 (Clock Synchronization, Timestamp Determinism & Cryptographic Audit Trails) |
| PostgreSQL Target | Session TimeZone Drift, Non-Deterministic Daylight Saving / Offset Shifts, and Audit Integrity Corruption |
| Default Status | enabled |
Temporal column definitions in database migration files (db/migrations/) must explicitly use TIMESTAMPTZ (timestamp with time zone) instead of bare TIMESTAMP (timestamp without time zone).
┌─────────────────────────────────────────────────────────────────────────────┐
│ ARCHITECTURAL INVARIANT │
│ │
│ Bare `TIMESTAMP` (without time zone) is strictly prohibited. │
│ │
│ A bare `TIMESTAMP` column stores wall-clock date and time without any │
│ timezone offset or epoch anchor, making timestamps ambiguous and dependent │
│ on whatever `TimeZone` GUC setting is active during insertion or query. │
│ │
│ All temporal points in time must use `TIMESTAMPTZ` (internally stored as │
│ UTC epoch microseconds) to preserve immutable chronological order. │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ THE BARE TIMESTAMP TIME-TRAVEL DISASTER │
│ │
│ Case A: Bare TIMESTAMP (VIOLATION): │
│ Column: `created_at TIMESTAMP` │
│ ├─► App Server A (WIB / UTC+7) inserts `2026-08-29 10:00:00` │
│ ├─► App Server B (UTC) inserts `2026-08-29 04:00:00` (1 hour later!) │
│ ├─► DB stores raw strings without timezone metadata │
│ ├─► Audit Query: Server A's record appears 6 hours IN THE FUTURE! │
│ └─► SEV-1: Cryptographic audit log corruption & TOCTOU sequence failures! │
│ │
│ Case B: Explicit TIMESTAMPTZ (COMPLIANT): │
│ Column: `created_at TIMESTAMPTZ` │
│ ├─► Server A inserts `10:00:00+07` -> Normalized to UTC: `03:00:00Z` │
│ ├─► Server B inserts `04:00:00Z` -> Normalized to UTC: `04:00:00Z` │
│ ├─► Chronological ordering is mathematically guaranteed (< 1 microsecond) │
│ └─► Deterministic audit logs across all distributed services! │
└─────────────────────────────────────────────────────────────────────────────┘
-
Ambiguous Local Time: Bare
TIMESTAMPdoes not preserve the offset of the client.2026-08-29 12:00:00could be noon in London, Tokyo, or Jakarta. -
Daylight Saving Time (DST) Overlaps: During autumn clock fallbacks (e.g. 2:00 AM
$\rightarrow$ 1:00 AM), events occurring in the overlapping hour cannot be ordered chronologically. - Audit Log Invalidation: In security-critical multi-tenant systems, non-deterministic timestamps invalidate signature chains and proof-of-authenticity audit logs.
- PostgreSQL internally converts all incoming
TIMESTAMPTZvalues to UTC epoch microseconds (8-byte integer) before storage on disk. - When queried, it converts UTC to the client's current session timezone for display, guaranteeing absolute universal time integrity regardless of where the query originated.
flowchart TD
A["Migration File (*.up.sql)"] --> B["Parse AST via pg_query_go"]
B --> C["Extract ColumnDef Nodes from CREATE TABLE & ALTER TABLE"]
C --> D{"Is Column Type 'timestamp' or 'pg_catalog.timestamp'?"}
D -- "No (TIMESTAMPTZ, DATE, TIME, etc.)" --> E["PASS"]
D -- "Yes" --> F{"Is Inline Ignore Directive Present?"}
F -- "Yes" --> G["PASS (Suppressed)"]
F -- "No" --> H["FAIL: ARGUS-A30 Bare TIMESTAMP Prohibited (CWE-361, CWE-662)"]
-
Table Column Parser: Walks all
ColumnDefnodes insideCreateStmt(inCREATE TABLE) andAlterTableCmd(inALTER TABLE ... ADD COLUMNandALTER TABLE ... ALTER COLUMN TYPE). -
Type Inspection: Inspects
TypeName.Names. Ifnames == ["timestamp"]or["pg_catalog", "timestamp"], it is a bare timestamp without time zone. -
Exemptions: pure
DATEandTIME(without date) types are allowed where timezone is irrelevant (e.g., recurring daily business hours).
-- VIOLATION: Bare timestamp lacks UTC anchor
CREATE TABLE events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);-- VIOLATION: Alter column type to bare timestamp
ALTER TABLE audit_logs ADD COLUMN archived_at TIMESTAMP;-- COMPLIANT: Explicit TIMESTAMPTZ
CREATE TABLE events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);-- COMPLIANT: Pure DATE or TIME for calendar/schedule concepts
CREATE TABLE shift_schedules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
shift_date DATE NOT NULL,
start_time TIME NOT NULL
);-
In Migration Files:
Always replace
TIMESTAMPwithTIMESTAMPTZ:ALTER TABLE users ADD COLUMN verified_at TIMESTAMPTZ;
-
Migrating Existing Columns:
Convert existing columns cleanly:
ALTER TABLE users ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'UTC';
rules:
ARGUS-A30:
enabled: trueCREATE TABLE legacy_raw_ticks (
id UUID PRIMARY KEY,
-- argus:ignore ARGUS-A30 legacy wall-clock duration ticks without timezone
tick_time TIMESTAMP NOT NULL
);