Skip to content

fix: make CORS origins configurable, default to same-origin only - #70

Merged
mwulffn merged 2 commits into
mainfrom
fix/configurable-cors-33
Sep 16, 2026
Merged

mwulffn merged 2 commits into
mainfrom
fix/configurable-cors-33

Conversation

@mwulffn

@mwulffn mwulffn commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #33.

backend/app/main.py hardcoded allow_origins=["*"]. In the shipped compose topology the frontend and API sit behind the same nginx proxy, so the browser never makes a cross-origin request in the first place — same-origin should be the default, with * as an explicit opt-in. allow_credentials was already False.

Changes

  • Settings.cors_allow_origins: str = "" (comma-separated, matching the config's existing plain-scalar style — a bare list[str] field would parse the env var as JSON) and a cors_origins() helper that parses it for CORSMiddleware.
  • Wired CORS_ALLOW_ORIGINS through docker-compose.yml, documented in .env.example and docs/getting-started/configuration.md, following the ENCRYPTION_KEY precedent.

A real regression this would otherwise have caused

frontend/.env has pointed VITE_ATS_API_BASE_URL at the absolute http://localhost:8000/api since the frontend was first added (2024), bypassing the proxy vite.config.js already sets up and the documented behavior (docs/architecture/frontend.md: "the dev server proxies API requests to the backend"). That made npm run dev against a locally-run backend a genuine cross-origin request — which the new same-origin default would have silently broken.

Fixed it to the documented relative /api, matching what docker-compose.override.yml already sets for the dockerized dev frontend. Local development now needs no CORS opt-in at all — it's same-origin everywhere: bare-metal dev, dockerized dev, and production.

Tests

backend/tests/test_cors.py:

  • Unit tests for the cors_origins() parser (empty, comma-separated, *).
  • Behavioral tests against a standalone CORSMiddleware app (the real app builds its middleware at import time, so varying settings against the global app isn't possible) confirming: the default sends no access-control-allow-origin header cross-origin; a configured origin is echoed back; other origins are rejected.

Verification

  • uv run ruff check . — clean
  • uv run pytest — 201 passed
  • npx eslint . --ext .vue,.js,.jsx,.cjs,.mjs --ignore-path .gitignore — clean

🤖 Generated with Claude Code

mwulffn and others added 2 commits September 7, 2026 11:13
backend/app/main.py hardcoded allow_origins=["*"], which is more
permissive than the shipped topology needs — the frontend and API sit
behind the same nginx proxy, so the browser never makes a cross-origin
request in the first place. allow_credentials was already False.

Add Settings.cors_allow_origins (comma-separated string, matching the
existing plain-scalar style in config.py — a bare list[str] field
would parse the env var as JSON) and a cors_origins() helper that
parses it into the list CORSMiddleware expects. Empty (the default) is
same-origin only; "*" is an explicit opt-in for anyone serving the
frontend from a separate origin.

Fixed a real regression this change would otherwise have caused:
frontend/.env has pointed VITE_ATS_API_BASE_URL at the absolute
http://localhost:8000/api since the frontend was first added, bypassing
the proxy vite.config.js already sets up and documented behavior
(docs/architecture/frontend.md says "the dev server proxies API
requests to the backend"). That made `npm run dev` against a local
backend a genuine cross-origin request, which the new same-origin
default would have blocked. Pointed it at the documented relative
/api instead, matching what docker-compose.override.yml already sets
for the dockerized dev frontend — so local dev needs no CORS opt-in at
all.

Wired CORS_ALLOW_ORIGINS through docker-compose.yml and documented it
in .env.example and docs/getting-started/configuration.md, alongside
the existing ENCRYPTION_KEY precedent.

Added tests/test_cors.py: unit tests for the parser, and behavioral
tests (via a standalone CORSMiddleware app, since the real app builds
its middleware at import time) confirming the default sends no
access-control-allow-origin header cross-origin, and a configured
origin is echoed back while others are rejected.

Closes #33

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TNWyAav5c2jAaXemCLrL9g
@mwulffn
mwulffn merged commit 2feb635 into main Sep 16, 2026
3 checks 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.

CORS is wide open and not configurable

1 participant