Skip to content

config: support multiple CORS origins and fail closed in production - #73

Merged
joelpeace48-cell merged 1 commit into
FinesseStudioLab:mainfrom
Codeinx:issue-2-cors-origin
Aug 25, 2026
Merged

joelpeace48-cell merged 1 commit into
FinesseStudioLab:mainfrom
Codeinx:issue-2-cors-origin

Conversation

@Codeinx

@Codeinx Codeinx commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Problem

src/config/env.ts declared:

CORS_ORIGIN: z.string().default("http://localhost:3000"),

Two problems: it's a single string, so the API can't serve a preview deployment and production at once; and the default is a development origin that silently applies in production if the variable is unset — a misconfiguration that fails open rather than closed.

What this does

  • CORS_ORIGIN is now comma-separated, parsed into an array (parseCorsOrigins) — multiple origins are supported at once.
  • No default in the schema. A superRefine on the env schema requires CORS_ORIGIN to be set explicitly whenever NODE_ENV === "production", and rejects "*" in production (alone or mixed into a list) — either failure throws at startup via parseEnv, not at the first request.
  • Outside production, an unset CORS_ORIGIN still falls back to the dev default (resolveCorsOrigins), so local development is unaffected.
  • @fastify/cors is now registered with credentials: true and an explicit methods list instead of leaving both implicit.

Testing

src/config/env.test.ts adds cases for: comma-separated parsing, the dev fallback, production rejecting a missing CORS_ORIGIN, production rejecting "*" (alone and mixed into a list), and production accepting a valid explicit origin.

npm run lint, npm run build, npm test, and npm audit --audit-level=high are all clean on this branch.

Scope note

Touches only src/config/env.ts, src/config/env.test.ts, and src/index.ts (the cors registration call). No other currently-open PR touches these files, so this should apply independently of merge order.

Closes #2.

CORS_ORIGIN was a single string defaulting to "http://localhost:3000"
— a preview deployment and production couldn't each have their own
origin, and an unset variable silently fell back to a development
origin in production instead of failing startup.

- CORS_ORIGIN is now comma-separated, parsed into a string array
  (parseCorsOrigins), so multiple origins are supported at once.
- No default in the schema. In production, CORS_ORIGIN must be set
  explicitly and must not contain "*" — enforced by a superRefine on
  the env schema, so a missing or wildcard value fails startup rather
  than the first request.
- Outside production, an unset CORS_ORIGIN still falls back to the
  dev default (resolveCorsOrigins) so local development is unaffected.
- @fastify/cors is now configured with credentials and an explicit
  methods list instead of leaving both implicit.

Tests cover comma-separated parsing, the dev fallback, the production
guard rejecting a missing value, the production guard rejecting "*"
(alone and mixed into a list), and production accepting a valid
explicit origin.

Closes FinesseStudioLab#2.
@joelpeace48-cell

Copy link
Copy Markdown
Contributor

Nice job @Codeinx

@joelpeace48-cell
joelpeace48-cell merged commit bb03054 into FinesseStudioLab:main Aug 25, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

config: CORS_ORIGIN accepts only one origin and defaults to localhost in production

2 participants