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
12 changes: 12 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM python:3.11-slim
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

Base image is Python 3.11, but the project declares requires-python ">=3.12" (pyproject.toml). This mismatch can cause runtime failures or dependency resolution issues in the container. Update the image to a 3.12 variant (or adjust the project requirement if 3.11 is actually supported).

Suggested change
FROM python:3.11-slim
FROM python:3.12-slim

Copilot uses AI. Check for mistakes.

WORKDIR /app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

EXPOSE 8000

CMD ["bash", "/app/start.sh"]
33 changes: 30 additions & 3 deletions backup/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import shutil
import logging
from contextlib import asynccontextmanager
from urllib.parse import urlsplit

from dotenv import load_dotenv
from fastapi import FastAPI
Expand All @@ -22,21 +23,47 @@
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 = [origin.strip() for origin in allowed_origins_env.split(",") if origin.strip()]
parsed = _normalized_unique(allowed_origins_env.split(","))

if parsed:
return parsed

# Backward-compatible fallback when comma-separated variable is not set.
legacy_origins = [o for o in [FRONTEND_DEV_URL, FRONTEND_PROD_URL] if o]
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", "*"]
return ["http://localhost:3000", "http://127.0.0.1:3000"]
Comment on lines +26 to +66
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

New origin normalization/allow-list building logic is security-sensitive and currently untested. Add tests that exercise _build_allowed_origins for env values with paths/trailing slashes, duplicates, invalid entries, and the legacy FRONTEND_DEV_URL/FRONTEND_PROD_URL fallback to ensure CORS behavior stays correct.

Copilot uses AI. Check for mistakes.


ALLOWED_ORIGINS = _build_allowed_origins()
Expand Down
1 change: 1 addition & 0 deletions pages/scoreboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ <h2>
<p>Laboratory of Research in Computer Science and Applications (LRSIA), 2026</p>
</footer>

<script src="static/js/config.js"></script>
<script src="static/js/scoreboard.js"></script>
</body>
</html>
2 changes: 1 addition & 1 deletion pages/static/js/auth.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const API_URL = "http://localhost:8000";
const API_URL = window.APP_CONFIG?.API_URL || "https://mpvrppythonapi.pinite37.me";
let authMode = 'login';

document.getElementById('mobile-menu').addEventListener('click', () => {
Expand Down
3 changes: 3 additions & 0 deletions pages/static/js/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
window.APP_CONFIG = Object.freeze({
API_URL: "https://mpvrppythonapi.pinite37.me"
});
3 changes: 1 addition & 2 deletions pages/static/js/scoreboard.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// load API_URL from .env file
const API_URL = 'http://localhost:8000';
const API_URL = window.APP_CONFIG?.API_URL || "https://mpvrppythonapi.pinite37.me";

// ── Nav mobile ───────────────────────────────────────────
document.getElementById('mobile-menu').addEventListener('click', () => {
Expand Down
3 changes: 2 additions & 1 deletion pages/submission.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ <h1>
</div>
<ul class="nav-links" id="nav-links">
<li><a href="../index.html">Home</a></li>
<li><a href="scoreboard.html">Leaderboard</a></li>
<li><a href="scoreboard.html">Scoreboard</a></li>
<li><a href="submission.html" class="active">Submission</a></li>
<li id="nav-team-name" style="display:none; color:#3C27F5; font-weight:bold; font-size:15px; padding: 0 5px;"></li>
<li id="nav-logout-li" style="display:none;">
Expand Down Expand Up @@ -108,6 +108,7 @@ <h3 style="margin-top:30px;">Your history</h3>
</footer>


<script src="static/js/config.js"></script>
<script src="static/js/auth.js"></script>
</body>
</html>
4 changes: 2 additions & 2 deletions start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ WORKERS="${WORKERS:-2}"

# 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}"
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

FRONTEND_ALLOWED_ORIGINS default includes path components (e.g. "/MPVRP-CC"), but CORS Origin values are scheme+host(+port) only. With the new normalization this collapses to a single origin and will emit warnings; it also omits localhost origins which can break local frontend dev when using start.sh defaults. Consider defaulting to origins only (no paths) and include localhost entries if local dev via start.sh is expected.

Suggested change
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_ALLOWED_ORIGINS="${FRONTEND_ALLOWED_ORIGINS:-https://ifri-ai-classes.github.io,http://localhost:3000,http://127.0.0.1:3000,http://localhost:5173,http://127.0.0.1:5173}"

Copilot uses AI. Check for mistakes.

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

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

export SECRET_KEY="$(...)" overwrites any pre-set SECRET_KEY on every start, which breaks token validity across restarts and contradicts the "Require stable secret key" comment. It also makes the subsequent -z check effectively dead code. Prefer only generating a key when SECRET_KEY is unset (or fail fast in production) and keep the stability requirement enforceable.

Copilot uses AI. Check for mistakes.
Expand Down
Loading