Skip to content

Validate environment variables at startup and remove insecure fallback defaults #207

@grantfox-development

Description

@grantfox-development

Problem

Several environment variables silently fall back to insecure defaults when unset, which is a real security risk in any non-local environment:

  • src/middleware/authMiddleware.ts:14const SECRET_KEY = process.env.JWT_SECRET || 'defaultSecret';
    → If JWT_SECRET is missing in production, every token is signed and verified with the literal string 'defaultSecret'. Anyone who knows the source code can forge tokens.
  • src/config/data-source.ts:10password: process.env.DB_PASSWORD || "password"
    → Falls back to the literal "password".
  • src/config/data-source.ts:7-11 — host/port/user/db all default silently.
  • src/utils/email.utils.ts — uses EMAIL_USER / EMAIL_PASS / FRONTEND_URL with no validation; missing values produce confusing runtime errors instead of a clear startup failure.
  • src/modules/shared/middleware/rate-limit/domain/rate-limit-config.ts — many Number(process.env.X) || N patterns; if X is a non-numeric string, the fallback is silently used.

There is no centralized place where required env vars are validated when the process boots, so misconfiguration is discovered late (often only when a request hits a code path that needs the var).

Proposed Solution

  1. Add a runtime env-var schema using zod (already a common choice and small) or envalid. Place it at src/config/env.ts.
  2. Define a schema that:
    • Marks JWT_SECRET, DATABASE_URL (or DB_*), REDIS_URL, EMAIL_USER, EMAIL_PASS, FRONTEND_URL as required in production.
    • Allows safe local defaults only when NODE_ENV !== 'production' and explicitly logs a warning.
    • Coerces numeric vars (PORT, all RATE_LIMIT_*) to number and rejects non-numeric input.
    • Validates NODE_ENV against the enum ['development', 'test', 'staging', 'production'].
  3. Export a typed, frozen env object and replace all direct process.env.X reads in src/ with imports from src/config/env.ts.
  4. Call the validator in src/index.ts before Express is constructed; on validation failure, log the missing/invalid keys and process.exit(1).
  5. Remove insecure literal fallbacks ('defaultSecret', "password", etc.).
  6. Update .env.example (create if missing) with the full required-vars list and short comments.
  7. Add a unit test that imports the schema, feeds it bad input, and asserts the validator throws.

Acceptance Criteria

  • src/config/env.ts exists, validates all required vars, and exports a typed env object.
  • No process.env.X reads remain inside src/ (except inside env.ts itself); grep returns clean.
  • JWT_SECRET || 'defaultSecret' and DB_PASSWORD || "password" are removed.
  • In production mode, missing required vars cause the process to exit with a clear error listing every missing/invalid key.
  • In development mode, sensible defaults are allowed but logged as a warning at startup.
  • .env.example enumerates every required and optional variable.
  • New unit test covers validator success, missing-required-in-prod, and bad-numeric cases.
  • All existing tests still pass.

Out of Scope

  • Secret-management / vault integration.
  • Rotating existing JWT secrets in deployed environments (ops concern).

Suggested Labels

security, enhancement, developer-experience

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions