Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 1 addition & 46 deletions backup/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import shutil
import logging
from contextlib import asynccontextmanager
from urllib.parse import urlsplit

from dotenv import load_dotenv
from fastapi import FastAPI
Expand All @@ -13,7 +12,6 @@
from backup.app.routes import generator, model, scoring, scoreboard, auth

load_dotenv()
FRONTEND_DEV_URL = os.getenv("FRONTEND_DEV_URL", "")
FRONTEND_PROD_URL = os.getenv("FRONTEND_PROD_URL", "")

logging.basicConfig(
Expand All @@ -23,50 +21,7 @@
logger = logging.getLogger(__name__)


def _normalize_origin(origin: str) -> str | None:
"""Normalize CORS entries to scheme://host[:port] and drop invalid values."""
raw = origin.strip()
if not raw:
return None

parsed = urlsplit(raw)
if not parsed.scheme or not parsed.netloc:
logger.warning("Ignoring invalid CORS origin '%s'", origin)
return None

normalized = f"{parsed.scheme}://{parsed.netloc}"
if normalized != raw.rstrip("/"):
logger.warning("Normalized CORS origin '%s' to '%s'", origin, normalized)
return normalized


def _normalized_unique(origins: list[str]) -> list[str]:
result: list[str] = []
for origin in origins:
normalized = _normalize_origin(origin)
if normalized and normalized not in result:
result.append(normalized)
return result


def _build_allowed_origins() -> list[str]:
"""Build a strict CORS allow-list from environment variables."""
allowed_origins_env = os.getenv("FRONTEND_ALLOWED_ORIGINS", "")
parsed = _normalized_unique(allowed_origins_env.split(","))

if parsed:
return parsed

# Backward-compatible fallback when comma-separated variable is not set.
legacy_origins = _normalized_unique([FRONTEND_DEV_URL, FRONTEND_PROD_URL])
if legacy_origins:
return legacy_origins

# Safe local fallback for development/test only.
return ["http://localhost:3000", "http://127.0.0.1:3000"]


ALLOWED_ORIGINS = _build_allowed_origins()
ALLOWED_ORIGINS = ["http://localhost:3000", "http://127.0.0.1:3000", "https://ifri-ai-classes.github.io", FRONTEND_PROD_URL]
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ALLOWED_ORIGINS currently always includes localhost origins and appends FRONTEND_PROD_URL even when it is an empty string. This broadens CORS in production and can also introduce an invalid origin entry. Consider rebuilding this list from environment configuration, filtering out falsy/invalid values, and only including localhost origins in dev/test.

Copilot uses AI. Check for mistakes.
logger.info("CORS allow-list configured with %s origin(s)", len(ALLOWED_ORIGINS))


Expand Down
6 changes: 3 additions & 3 deletions start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ cd "$SCRIPT_DIR"
# Production-safe defaults (override with env vars).
HOST="${HOST:-0.0.0.0}"
PORT="${PORT:-8000}"
WORKERS="${WORKERS:-2}"
WORKERS="${WORKERS:-1}"

# Export DATABASE_URL (uses existing value if already set).
export DATABASE_URL="${DATABASE_URL:-sqlite:///./mpvrp_scoring.db}"
export FRONTEND_ALLOWED_ORIGINS="${FRONTEND_ALLOWED_ORIGINS:-https://ifri-ai-classes.github.io,https://ifri-ai-classes.github.io/MPVRP-CC,https://ifri-ai-classes.github.io/MPVRP-CC/pages}"
export FRONTEND_PROD_URL="${FRONTEND_PROD_URL:-https://ifri-ai-classes.github.io}"

# Require stable secret key in environments with external users.
# Generate and export a fresh SECRET_KEY at launch time.
export SECRET_KEY="$(python -c "import secrets; print(secrets.token_urlsafe(32))")"
export SECRET_KEY="X2ZlC8ezhVReYCer02s7TdwRT10epQMjwZVKAFwTOE4"
if [[ -z "${SECRET_KEY:-}" ]]; then
echo "ERROR: SECRET_KEY is required. Set it in your environment before starting the server." >&2
Comment on lines 17 to 21
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoding SECRET_KEY in the repo is a security issue and also makes the subsequent "SECRET_KEY is required" check ineffective (it can never be empty). Prefer reading SECRET_KEY from the environment/secret manager (and failing fast if missing), and avoid committing real secrets to version control. Also update/remove the nearby comment that says a fresh key is generated at launch, since the script now sets a fixed value.

Copilot uses AI. Check for mistakes.
exit 1
Expand Down
Loading