From 1194fe241860477643a2736e8a1485a7ef823539 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 5 Jul 2026 11:25:34 +0000 Subject: [PATCH] Fix overly permissive CORS configuration Co-authored-by: benpiper <4343814+benpiper@users.noreply.github.com> --- .jules/sentinel.md | 4 ++++ backend/main.py | 33 ++++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index de9f3d8..17e0f9f 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -15,3 +15,7 @@ **Vulnerability:** The `extract_project` endpoint in `backend/main.py` used a generic exception handler that directly returned the string representation of any caught exception (via `detail=str(e)`) in a 400 Bad Request response. **Learning:** Returning `str(e)` directly to API clients can unintentionally expose internal system details, tracebacks, database schema info, or file paths, leading to Information Leakage. Additionally, placing a broad `except Exception` without specifically handling `HTTPException` first will swallow intentional API errors. **Prevention:** Always log the full exception securely server-side using `logger.error(..., exc_info=True)` and return a generic "Internal server error" string to the client. Ensure `except HTTPException: raise` is placed before the catch-all `Exception` block. +## 2024-05-02 - [HIGH] Overly permissive CORS configuration allowing wildcards and invalid URLs +**Vulnerability:** The `get_cors_origins` function accepted any string from the `CORS_ORIGINS` environment variable, which could include wildcards (`*`) or invalid URL structures. Allowing `*` when `allow_credentials=True` is enabled in `CORSMiddleware` causes application startup/runtime errors, and allowing arbitrary strings bypasses intended origin restrictions. +**Learning:** When generating a list of allowed CORS origins from environment variables, all origins must be strictly validated. Furthermore, wildcard origins must be explicitly rejected if the API intends to support credentials (cookies/authorization headers). +**Prevention:** Use `urllib.parse.urlparse` to validate that each candidate origin has a valid scheme (`http` or `https`) and a valid host (`netloc`). Explicitly filter out the `*` wildcard origin before passing the list to `CORSMiddleware`. diff --git a/backend/main.py b/backend/main.py index f637f4a..63087fc 100644 --- a/backend/main.py +++ b/backend/main.py @@ -13,6 +13,7 @@ from pydantic import BaseModel, Field from pathlib import Path import time +import urllib.parse from scanner import scan_directory_for_videos from extractor import async_extract_keyframes_parallel, async_get_video_duration @@ -146,17 +147,31 @@ async def lifespan(app: FastAPI): def get_cors_origins(): """Build CORS origins list, auto-detecting Render deployments.""" explicit = os.getenv("CORS_ORIGINS") - if explicit: - return [o.strip() for o in explicit.split(",") if o.strip()] - - origins = ["http://localhost:5173", "http://localhost:3000"] - # Auto-detect Render deployments: add the frontend service URL - frontend_url = os.getenv("FRONTEND_URL") - if frontend_url: - origins.append(frontend_url) + raw_origins = [] + if explicit: + raw_origins = [o.strip() for o in explicit.split(",") if o.strip()] + else: + raw_origins = ["http://localhost:5173", "http://localhost:3000"] + + # Auto-detect Render deployments: add the frontend service URL + frontend_url = os.getenv("FRONTEND_URL") + if frontend_url: + raw_origins.append(frontend_url) + + valid_origins = [] + for origin in raw_origins: + if origin == "*": + # Skip wildcard to prevent runtime errors with allow_credentials=True + continue + try: + parsed = urllib.parse.urlparse(origin) + if parsed.scheme in ["http", "https"] and parsed.netloc: + valid_origins.append(origin) + except Exception: + pass - return origins + return valid_origins cors_origins = get_cors_origins() app.add_middleware(