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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions