From 2afa1d8c1d16dc7e5789419f070a0ce67624145b Mon Sep 17 00:00:00 2001 From: Moore Date: Mon, 29 Jun 2026 14:17:49 +0100 Subject: [PATCH 1/3] fix(#709): Enable TLS certificate validation in production - Set rejectUnauthorized: true in production SSL config - Add DB_SSL_CA environment variable for CA certificate path - Validate DB_SSL_CA is provided on module load in production - Development continues to allow unverified certificates - Document required environment variables in .env.example Fixes: Prevents man-in-the-middle attacks by enforcing TLS certificate verification in production database connections. --- .env.example | 6 ++++++ src/lib/db/pool.ts | 28 +++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index ae3ccd6a..c3b41639 100644 --- a/.env.example +++ b/.env.example @@ -31,6 +31,12 @@ DB_POOL_MAX=20 DB_CONNECTION_TIMEOUT=5000 DB_IDLE_TIMEOUT=30000 +# Database SSL Configuration (Required in production) +# Path to CA certificate file for TLS verification +# In production, this must be set to enable secure certificate validation +# Example: /etc/ssl/certs/ca-bundle.crt or /path/to/ca.pem +DB_SSL_CA= + # SMS Integration (#448) # Provider selection: twilio | sns | vonage (default: twilio) SMS_PROVIDER=twilio diff --git a/src/lib/db/pool.ts b/src/lib/db/pool.ts index f1fc9dd8..dfa312fc 100644 --- a/src/lib/db/pool.ts +++ b/src/lib/db/pool.ts @@ -13,12 +13,38 @@ import { retryWithBackoff } from '@/utils/errorUtils'; * - Query queueing during reconnect windows */ +/** + * Validate DB_SSL_CA is provided in production + */ +function validateSSLConfig(): void { + if (process.env.NODE_ENV === 'production' && !process.env.DB_SSL_CA) { + throw new Error( + 'DB_SSL_CA environment variable is required in production. ' + + 'This should contain the path to your CA certificate file.', + ); + } +} + +// Validate on module load +validateSSLConfig(); + +const getSSLConfig = () => { + if (process.env.NODE_ENV === 'production') { + return { + rejectUnauthorized: true, + ca: process.env.DB_SSL_CA, + }; + } + // Allow unverified certificates in development + return false; +}; + const DB_CONFIG: PoolConfig = { connectionString: process.env.DATABASE_URL, max: parseInt(process.env.DB_POOL_MAX || '20', 10), connectionTimeoutMillis: parseInt(process.env.DB_CONNECTION_TIMEOUT || '5000', 10), idleTimeoutMillis: parseInt(process.env.DB_IDLE_TIMEOUT || '30000', 10), - ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, + ssl: getSSLConfig(), }; type CircuitState = 'CLOSED' | 'OPEN'; From ea31a77b3ee1eb300f03822d1fd11f9f5a1bcdfd Mon Sep 17 00:00:00 2001 From: Moore Date: Thu, 2 Jul 2026 10:09:27 +0100 Subject: [PATCH 2/3] fix: defer DB_SSL_CA validation to runtime to allow build without environment variables --- src/lib/db/pool.ts | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/lib/db/pool.ts b/src/lib/db/pool.ts index dfa312fc..84374c33 100644 --- a/src/lib/db/pool.ts +++ b/src/lib/db/pool.ts @@ -13,23 +13,14 @@ import { retryWithBackoff } from '@/utils/errorUtils'; * - Query queueing during reconnect windows */ -/** - * Validate DB_SSL_CA is provided in production - */ -function validateSSLConfig(): void { - if (process.env.NODE_ENV === 'production' && !process.env.DB_SSL_CA) { - throw new Error( - 'DB_SSL_CA environment variable is required in production. ' + - 'This should contain the path to your CA certificate file.', - ); - } -} - -// Validate on module load -validateSSLConfig(); - const getSSLConfig = () => { if (process.env.NODE_ENV === 'production') { + if (!process.env.DB_SSL_CA) { + throw new Error( + 'DB_SSL_CA environment variable is required in production. ' + + 'This should contain the path to your CA certificate file.', + ); + } return { rejectUnauthorized: true, ca: process.env.DB_SSL_CA, From ef3ca8da2839b7021763be151a10376e0e8e0026 Mon Sep 17 00:00:00 2001 From: Moore Date: Thu, 2 Jul 2026 10:30:02 +0100 Subject: [PATCH 3/3] fix: make database config lazy-loaded to allow build without DB_SSL_CA --- src/lib/db/pool.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/db/pool.ts b/src/lib/db/pool.ts index 84374c33..894d927b 100644 --- a/src/lib/db/pool.ts +++ b/src/lib/db/pool.ts @@ -30,13 +30,13 @@ const getSSLConfig = () => { return false; }; -const DB_CONFIG: PoolConfig = { +const getDbConfig = (): PoolConfig => ({ connectionString: process.env.DATABASE_URL, max: parseInt(process.env.DB_POOL_MAX || '20', 10), connectionTimeoutMillis: parseInt(process.env.DB_CONNECTION_TIMEOUT || '5000', 10), idleTimeoutMillis: parseInt(process.env.DB_IDLE_TIMEOUT || '30000', 10), ssl: getSSLConfig(), -}; +}); type CircuitState = 'CLOSED' | 'OPEN'; @@ -61,7 +61,7 @@ class DatabasePool { public static getInstance(): Pool { if (!DatabasePool.instance) { - DatabasePool.instance = new Pool(DB_CONFIG); + DatabasePool.instance = new Pool(getDbConfig()); DatabasePool.instance.on('connect', () => { if (process.env.NODE_ENV === 'development') {