# ARGUS-A12: Database Connection Pool Timeout Configurations > **Rule Code:** `ARGUS-A12` > **Identifier:** `TIMEOUT_CONFIG` > **Severity:** `HIGH / CRITICAL` (Runaway Query & Connection Starvation Blocker) > **Category:** `Operational Stability, Resource Bounds & DoS Prevention` > **Target Standards:** CWE-400 (Uncontrolled Resource Consumption), OWASP ASVS v4.0.3/v5.0 §V1.4.3, §V11.1.4 --- ## 1. Overview & Core Invariant Every PostgreSQL connection pool initialization (`pgxpool.Config`, `pgxpool.ParseConfig`, or DSN connection string) **must explicitly configure server-side and client-side timeout boundaries**, including: 1. **`statement_timeout`**: Cancels individual queries exceeding safe execution limits (e.g. 5,000-15,000 ms). 2. **`lock_timeout`**: Cancels queries waiting in lock acquisition queues beyond a strict threshold (e.g. 2,000-5,000 ms). 3. **`idle_in_transaction_session_timeout`**: Terminates connections abandoned while holding an open transaction (e.g. 10,000-15,000 ms). 4. **`MaxConnIdleTime` & `MaxConnLifetime`**: Enforces driver-side connection cycling to prevent memory bloat. Timeout parameters must not be omitted or explicitly disabled (`0` / unlimited). --- ## 2. Technical Grounding & PostgreSQL Engine Realities ### 2.1. Why Server-Level `postgresql.conf` is Insufficient for OLTP 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. ### 2.2. The Role of `lock_timeout` in Preventing Starvation 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. ### 2.3. Autovacuum Protection (`idle_in_transaction_session_timeout`) 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). ```mermaid flowchart TD subgraph HIERARCHY ["4-Tier PostgreSQL Timeout Hierarchy"] direction TB L["1. lock_timeout (e.g. 2s - 5s)
Fails fast if blocked in row/table lock queue"] S["2. statement_timeout (e.g. 5s - 15s)
Aborts query execution if compute exceeds threshold"] I["3. idle_in_transaction_session_timeout (e.g. 10s - 15s)
Terminates abandoned open transactions & protects autovacuum xmin"] T["4. transaction_timeout (e.g. 30s) [PostgreSQL 17+]
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?
statement_timeout=5000&
lock_timeout=2000&
idle_in_transaction_session_timeout=10000"] end ``` --- ## 3. How Argus Detects Violations (Static Analysis Architecture) Argus inspects connection pool initialization via DSN query parameters and Go struct assignment flows: ```mermaid flowchart LR Scan["Inspect Pool Init Calls
(Exclude _test.go)"] --> CallType{"pgxpool.New or
pgxpool.NewWithConfig?"} CallType -->|pgxpool.New| CheckDSN["dsn_checker.go:
Parse URL Query / KV Params"] CallType -->|pgxpool.NewWithConfig| CheckFlow["config_flow.go:
Track AST Assignments on Config Var"] CheckDSN --> DSNMissing{"Missing statement_timeout,
lock_timeout, or idle timeout?"} CheckFlow --> StructMissing{"Missing RuntimeParams or
MaxConnIdleTime / Lifetime?"} DSNMissing -->|Yes| ReportDSN["Report HIGH Violation:
Missing DSN Timeout Parameters"] StructMissing -->|Yes| ReportStruct["Report HIGH Violation:
Incomplete pgxpool.Config Timeouts"] DSNMissing -->|No| Pass["Pass (Complete Timeout Config)"] StructMissing -->|No| Pass ``` 1. **DSN Parameter Checker (`dsn_checker.go`):** Parses URL query parameters and key-value connection strings for `statement_timeout`, `lock_timeout`, and `idle_in_transaction_session_timeout`. 2. **Configuration Flow Evaluator (`config_flow.go`):** Tracks `pgxpool.Config` composite literals, struct assignments (`AssignStmt`), and helper initializers (`configurePostgresPool`). --- ## 4. Vulnerability & Risk Taxonomy | 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** | --- ## 5. Non-Compliant Code Patterns (Bad Examples) ### Example 1: Plain DSN Without Timeouts ```go // 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") } ``` ### Example 2: Incomplete `pgxpool.Config` ```go // 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) } ``` --- ## 6. Compliant Implementation Patterns (Good Examples) ### Solution 1: Fully Configured DSN ```go // 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) } ``` ### Solution 2: Explicit `pgxpool.Config` with Helper ```go // 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) } ``` --- ## 7. How to Suppress (Ignore Directives) For offline database maintenance utilities, analytical dump jobs, or batch ETL scripts: ```go // argus:ignore ARGUS-A12 analytical batch worker dedicated pool pool, err := pgxpool.NewWithConfig(ctx, batchCfg) ``` Alternatively, use the canonical identifier alias: ```go // argus:ignore TIMEOUT_CONFIG offline database dump utility pool, err := pgxpool.New(ctx, dumpDSN) ``` --- ## 8. Configuration Reference (`.argus.yaml`) Enable or configure timeout defaults in `.argus.yaml`: ```yaml rules: ARGUS-A12: enabled: true ```