Skip to content

Tighten CORS in production: replace allow_origins=["*"] with the already-built _cors_origins list #136

Description

@Sagargupta16

Problem

backend/src/ledger_sync/api/main.py carefully builds a _cors_origins list from settings.cors_origins plus the parsed settings.frontend_url, then ignores it and passes allow_origins=["*"] to CORSMiddleware:

_cors_origins = list(settings.cors_origins)
if settings.frontend_url:
    _parsed = urlparse(settings.frontend_url)
    _frontend_origin = f"{_parsed.scheme}://{_parsed.netloc}"
    if _frontend_origin and _frontend_origin not in _cors_origins:
        _cors_origins.append(_frontend_origin)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],          # ← the careful build-up above is dead code
    allow_credentials=False,
    allow_methods=["*"],
    allow_headers=["*"],
)

Why it matters

  • The app is JWT-authenticated with the access token stored in localStorage.
  • allow_origins=["*"] lets any website call this API.
  • allow_credentials=False blocks cookies, but a stolen JWT (e.g. XSS on any third-party origin that imports a compromised script) still works against the API because the script has access to localStorage.
  • _cors_origins is already being built — the fix is one line.

Suggested fix

Replace allow_origins=["*"] with allow_origins=_cors_origins.

Why this needs care (not a quick PR)

Tightening CORS without first verifying the production environment will break the live site. Before merging:

  1. Confirm LEDGER_SYNC_FRONTEND_URL on Vercel is set to the canonical frontend URL (https://sagargupta.online/ledger-sync or similar).
  2. Confirm LEDGER_SYNC_CORS_ORIGINS env var (JSON array) includes every domain the frontend is actually served from:
    • https://sagargupta.online
    • https://sagargupta16.github.io (GitHub Pages default)
    • any other custom domains
  3. Test from each production origin after deploy.
  4. Consider whether allow_credentials should be flipped on (currently False) once tokens move to httpOnly cookies — separate concern.

References

  • File: backend/src/ledger_sync/api/main.py (around lines 200-215)
  • Found during a code audit, May 2026.

Acceptance criteria

  • CORS allowed origins match the actual deployed frontends, no wildcard.
  • No regression on https://sagargupta.online/ledger-sync after deploy.
  • OPTIONS preflight still works for upload (multipart) and JSON endpoints.
  • Decide whether allow_credentials and httpOnly-cookie auth should land in the same change or a follow-up.

Labels

security, backend, low-priority

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendBackend (FastAPI/Python)securitySecurity hardening

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions