-
Notifications
You must be signed in to change notification settings - Fork 0
ARGUS A12
Rule Code:
ARGUS-A12Identifier:TIMEOUT_CONFIGSeverity:HIGH / CRITICAL(Runaway Query & Connection Starvation Blocker) Category:Operational Stability, Resource Bounds & DoS PreventionTarget Standards: CWE-400 (Uncontrolled Resource Consumption), OWASP ASVS v4.0.3/v5.0 §V1.4.3, §V11.1.4
Every PostgreSQL connection pool initialization (pgxpool.Config, pgxpool.ParseConfig, or DSN connection string) must explicitly configure server-side and client-side timeout boundaries, including:
-
statement_timeout: Cancels individual queries exceeding safe execution limits (e.g. 5,000-15,000 ms). -
lock_timeout: Cancels queries waiting in lock acquisition queues beyond a strict threshold (e.g. 2,000-5,000 ms). -
idle_in_transaction_session_timeout: Terminates connections abandoned while holding an open transaction (e.g. 10,000-15,000 ms). -
MaxConnIdleTime&MaxConnLifetime: Enforces driver-side connection cycling to prevent memory bloat.
Timeout parameters must not be omitted or explicitly disabled (0 / unlimited).
Server-wide timeout defaults in postgresql.conf are frequently configured loosely (or disabled at 0) to accommodate administrative tasks, batch ETL, or long-running database migrations. Application connection pools serving user traffic must not inherit loose timeouts: a single runaway query can exhaust connection pool slots and cascade into a cluster freeze.
When a transaction attempts to modify a row locked by another worker, it enters an unbuffered wait queue in PostgreSQL shared memory. Without lock_timeout, the blocked query hangs indefinitely until statement_timeout expires or the pool runs out of connections. lock_timeout forces a fast failure (2-3 seconds), allowing the application to retry or return a human-friendly response.
If an application worker crashes or blocks on external I/O inside an open transaction, the connection remains idle in transaction. This state pins the PostgreSQL global xmin transaction horizon, preventing autovacuum from reclaiming dead tuples cluster-wide and causing catastrophic disk bloat (CWE-400).
flowchart TD
subgraph HIERARCHY ["4-Tier PostgreSQL Timeout Hierarchy"]
direction TB
L["1. lock_timeout (e.g. 2s - 5s)<br/>Fails fast if blocked in row/table lock queue"]
S["2. statement_timeout (e.g. 5s - 15s)<br/>Aborts query execution if compute exceeds threshold"]
I["3. idle_in_transaction_session_timeout (e.g. 10s - 15s)<br/>Terminates abandoned open transactions & protects autovacuum xmin"]
T["4. transaction_timeout (e.g. 30s) [PostgreSQL 17+]<br/>Cumulative timeout from BEGIN to COMMIT"]
L --> S --> I --> T
end
subgraph DSN ["DSN Connection String Example"]
direction TB
DSN_STR["postgres://user:pass@host:5432/db?<br/>statement_timeout=5000&<br/>lock_timeout=2000&<br/>idle_in_transaction_session_timeout=10000"]
end
Argus inspects connection pool initialization via DSN query parameters and Go struct assignment flows:
flowchart LR
Scan["Inspect Pool Init Calls<br/>(Exclude _test.go)"] --> CallType{"pgxpool.New or<br/>pgxpool.NewWithConfig?"}
CallType -->|pgxpool.New| CheckDSN["dsn_checker.go:<br/>Parse URL Query / KV Params"]
CallType -->|pgxpool.NewWithConfig| CheckFlow["config_flow.go:<br/>Track AST Assignments on Config Var"]
CheckDSN --> DSNMissing{"Missing statement_timeout,<br/>lock_timeout, or idle timeout?"}
CheckFlow --> StructMissing{"Missing RuntimeParams or<br/>MaxConnIdleTime / Lifetime?"}
DSNMissing -->|Yes| ReportDSN["Report HIGH Violation:<br/>Missing DSN Timeout Parameters"]
StructMissing -->|Yes| ReportStruct["Report HIGH Violation:<br/>Incomplete pgxpool.Config Timeouts"]
DSNMissing -->|No| Pass["Pass (Complete Timeout Config)"]
StructMissing -->|No| Pass
-
DSN Parameter Checker (
dsn_checker.go): Parses URL query parameters and key-value connection strings forstatement_timeout,lock_timeout, andidle_in_transaction_session_timeout. -
Configuration Flow Evaluator (
config_flow.go): Trackspgxpool.Configcomposite literals, struct assignments (AssignStmt), and helper initializers (configurePostgresPool).
| Failure Mode | Technical Impact | Risk Severity |
|---|---|---|
Missing statement_timeout |
Runaway complex queries consume CPU/memory and hang connection slots indefinitely. | CRITICAL |
Missing lock_timeout |
Lock contention starves connection pool workers, blocking incoming traffic. | HIGH |
Missing idle_in_transaction |
Rogue open transactions stall autovacuum xmin horizon, triggering severe table bloat. |
CRITICAL |
Zero Timeout ("0") |
Explicitly disables timeout protection, exposing server to denial of service. | HIGH |
// VIOLATION: DSN missing statement_timeout, lock_timeout, and idle_in_transaction
func InitPool(ctx context.Context, dsn string) (*pgxpool.Pool, error) {
// Flagged: pgxpool DSN missing required timeout parameters
return pgxpool.New(ctx, "postgres://app:secret@localhost:5432/app_db")
}// VIOLATION: Config missing lock_timeout and MaxConnLifetime
func InitCustomPool(ctx context.Context, dsn string) (*pgxpool.Pool, error) {
cfg, err := pgxpool.ParseConfig(dsn)
if err != nil {
return nil, err
}
// Flagged: missing lock_timeout and MaxConnLifetime
cfg.ConnConfig.RuntimeParams = map[string]string{
"statement_timeout": "5000",
}
cfg.MaxConnIdleTime = 5 * time.Minute
return pgxpool.NewWithConfig(ctx, cfg)
}// COMPLIANT: All required timeout parameters present in connection string
const dsn = "postgres://app:secret@localhost:5432/app_db?" +
"statement_timeout=5000&" +
"lock_timeout=2000&" +
"idle_in_transaction_session_timeout=10000"
func InitPool(ctx context.Context) (*pgxpool.Pool, error) {
return pgxpool.New(ctx, dsn)
}// COMPLIANT: Complete RuntimeParams and pool lifecycle configuration
func NewDatabasePool(ctx context.Context, dsn string) (*pgxpool.Pool, error) {
cfg, err := pgxpool.ParseConfig(dsn)
if err != nil {
return nil, err
}
cfg.ConnConfig.RuntimeParams = map[string]string{
"statement_timeout": "10000",
"lock_timeout": "3000",
"idle_in_transaction_session_timeout": "15000",
}
cfg.MaxConnIdleTime = 5 * time.Minute
cfg.MaxConnLifetime = 1 * time.Hour
cfg.MaxConns = 25
return pgxpool.NewWithConfig(ctx, cfg)
}For offline database maintenance utilities, analytical dump jobs, or batch ETL scripts:
// argus:ignore ARGUS-A12 analytical batch worker dedicated pool
pool, err := pgxpool.NewWithConfig(ctx, batchCfg)Alternatively, use the canonical identifier alias:
// argus:ignore TIMEOUT_CONFIG offline database dump utility
pool, err := pgxpool.New(ctx, dumpDSN)Enable or configure timeout defaults in .argus.yaml:
rules:
ARGUS-A12:
enabled: true