Skip to content

P0: Secure CORS default instead of '*' (#167)#184

Open
dkijania wants to merge 1 commit into
mainfrom
feat/secure-cors
Open

P0: Secure CORS default instead of '*' (#167)#184
dkijania wants to merge 1 commit into
mainfrom
feat/secure-cors

Conversation

@dkijania

Copy link
Copy Markdown
Contributor

What & why

Part of the production-readiness epic (#163). Closes #167.

CORS defaulted to origin: process.env.CORS_ORIGIN ?? '*' — out of the box the API allowed cross-origin browser access from any origin. For a publicly reachable endpoint, wide-open CORS should be a deliberate choice, not the fallback.

Behavior

CORS_ORIGIN now resolves to a secure default:

CORS_ORIGIN Result
unset / empty CORS disabled — same-origin only
* allow any origin (explicit opt-in)
https://a.com,https://b.com allowlist of those origins

Server-to-server clients and curl are unaffected — CORS only governs browsers calling from another origin. A value that parses to no origins (e.g. just commas) falls back to the secure default rather than an empty, surprising allowlist.

Note on behavior change

This changes the out-of-the-box default from "any origin" to "same-origin only". Deployments that rely on browser cross-origin access must now set CORS_ORIGIN explicitly (* or an allowlist). This is documented in getting-started.md; the Compose example keeps * for local-dev convenience with a comment flagging it.

Changes

  • New src/server/cors.tsresolveCorsOptions(env) (isolated, unit-tested).
  • server.ts uses cors: resolveCorsOptions().

Testing

  • npm run build — clean
  • npm run test:unit — all pass (6 new CORS cases)
  • npm run lint — clean
  • npx prettier --debug-check . — exit 0

🤖 Generated with Claude Code

CORS defaulted to `origin: process.env.CORS_ORIGIN ?? '*'`, so out of the box
the API allowed cross-origin browser access from anywhere. For a publicly
reachable endpoint that wide-open default should be a deliberate choice, not
the fallback.

Resolve the Yoga `cors` option from CORS_ORIGIN with a secure default:

- unset / empty            → CORS disabled (same-origin only)
- `*`                      → allow any origin (explicit opt-in)
- comma-separated origins  → allowlist

Server-to-server clients and curl are unaffected — this only governs browsers
calling from another origin. The parsing lives in a small, unit-tested module;
a value that parses to no origins falls back to the secure default rather than
an empty, surprising allowlist. Docs and env example updated, including a note
that production should leave CORS_ORIGIN unset or use an allowlist.

Closes #167.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QSuak9smCHbp4N17xjjLF6
@dkijania dkijania added production-readiness Work toward making the API production-ready / publicly available P0 Blocker for public availability labels Jun 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P0 Blocker for public availability production-readiness Work toward making the API production-ready / publicly available

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P0: Secure CORS default instead of '*'

1 participant