security: harden production configuration and routes
Run Tests on Branches / Detect Changes (push) Successful in 12s
Run Tests on Branches / Frontend Tests (push) Successful in 2m12s
Run Tests on Branches / Frontend Mobile Tests (push) Has been skipped
Run Tests on Branches / Parapharmacy API Tests (push) Successful in 2m2s
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
Run Tests on Branches / Backend Tests (push) Successful in 2m8s
Run Tests on Branches / Detect Changes (push) Successful in 12s
Run Tests on Branches / Frontend Tests (push) Successful in 2m12s
Run Tests on Branches / Frontend Mobile Tests (push) Has been skipped
Run Tests on Branches / Parapharmacy API Tests (push) Successful in 2m2s
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
Run Tests on Branches / Backend Tests (push) Successful in 2m8s
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import { describe, expect, test } from '@jest/globals'
|
||||
import { validateProductionEnv } from '../src/config/required-env.js'
|
||||
|
||||
describe('validateProductionEnv', () => {
|
||||
test('rejects a missing production session secret', () => {
|
||||
expect(() => validateProductionEnv({
|
||||
NODE_ENV: 'production',
|
||||
CORS_ORIGIN: 'https://app.example.com',
|
||||
PG_URL: 'postgres://app:password@db/app',
|
||||
})).toThrow(/SESSION_SECRET/i)
|
||||
})
|
||||
|
||||
test('rejects placeholder production configuration', () => {
|
||||
expect(() => validateProductionEnv({
|
||||
NODE_ENV: 'production',
|
||||
SESSION_SECRET: 'farma-clic-secret-key-change-in-production',
|
||||
CORS_ORIGIN: 'http://localhost:3000',
|
||||
PG_URL: 'postgres://app:password@db/app',
|
||||
})).toThrow(/placeholder|production/i)
|
||||
})
|
||||
|
||||
test('accepts complete non-placeholder production configuration', () => {
|
||||
expect(() => validateProductionEnv({
|
||||
NODE_ENV: 'production',
|
||||
SESSION_SECRET: 'a-test-only-long-session-secret',
|
||||
CORS_ORIGIN: 'https://app.example.com',
|
||||
PG_URL: 'postgres://app:password@db/app',
|
||||
})).not.toThrow()
|
||||
})
|
||||
})
|
||||
@@ -26,6 +26,9 @@ import multer from 'multer';
|
||||
import { searchMedicines, getMedicineDetails, searchOTC } from './cima-service.js';
|
||||
import { runFarmaciaWebhookImport, DEFAULT_FARMACIAS_WEBHOOK, importPharmaciesFromRows } from './farmacias-webhook-import.js';
|
||||
import { fetchPharmaciesExternal } from '../API/index.js';
|
||||
import { validateProductionEnv } from './src/config/required-env.js';
|
||||
|
||||
validateProductionEnv();
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -81,11 +84,11 @@ if (PG_URL) {
|
||||
}
|
||||
|
||||
const sessionConfig = {
|
||||
secret: process.env.SESSION_SECRET || 'farma-clic-secret-key-change-in-production',
|
||||
secret: process.env.SESSION_SECRET || (process.env.NODE_ENV === 'test' ? 'test-only-session-secret' : undefined),
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
secure: process.env.COOKIE_SECURE === 'true',
|
||||
secure: process.env.NODE_ENV === 'production' || process.env.COOKIE_SECURE === 'true',
|
||||
sameSite: 'lax',
|
||||
httpOnly: true,
|
||||
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
||||
@@ -2648,4 +2651,3 @@ if (process.env.NODE_ENV !== 'test') {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
const PLACEHOLDERS = new Set([
|
||||
'',
|
||||
'change-me-in-production',
|
||||
'farma-clic-secret-key-change-in-production',
|
||||
'replace-me',
|
||||
'dev-ingest-key-change-me',
|
||||
])
|
||||
|
||||
export function validateProductionEnv(env = process.env) {
|
||||
if (env.NODE_ENV !== 'production') return
|
||||
|
||||
const required = [
|
||||
['SESSION_SECRET', env.SESSION_SECRET],
|
||||
['CORS_ORIGIN', env.CORS_ORIGIN],
|
||||
]
|
||||
|
||||
for (const [name, value] of required) {
|
||||
if (!value || PLACEHOLDERS.has(value)) {
|
||||
throw new Error(`${name} must be set to a non-placeholder value in production`)
|
||||
}
|
||||
}
|
||||
|
||||
if (env.CORS_ORIGIN.includes('localhost') || env.CORS_ORIGIN.includes('127.0.0.1')) {
|
||||
throw new Error('CORS_ORIGIN must not point to localhost in production')
|
||||
}
|
||||
|
||||
if (!env.PG_URL && (!env.PG_PASSWORD || PLACEHOLDERS.has(env.PG_PASSWORD))) {
|
||||
throw new Error('PG_URL or PG_PASSWORD must be set to a non-placeholder value in production')
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user