-
Notifications
You must be signed in to change notification settings - Fork 0
🛡️ Sentinel: [CRITICAL] Fix missing auth on migrate-formats #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,12 @@ | |
| raise ValueError("SECRET_KEY is required in production") | ||
| SECRET_KEY = "cricket-dev-key-2026" | ||
|
|
||
| ADMIN_SECRET = os.getenv("ADMIN_SECRET") | ||
| if not ADMIN_SECRET: | ||
| if APP_ENV in ("production", "prod"): | ||
| raise ValueError("ADMIN_SECRET is required in production") | ||
| ADMIN_SECRET = "super-secret-admin-key" | ||
|
|
||
|
Comment on lines
+14
to
+19
|
||
| ALGORITHM = "HS256" | ||
| ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "1440")) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,29 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from fastapi.testclient import TestClient | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from backend.main import app | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from backend.core.config import ADMIN_SECRET | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from backend.data.database import init_db | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Initialize DB for tests | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| init_db() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| client = TestClient(app) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_migrate_formats_no_header(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Expect 422 because the header is required (Header(...)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response = client.get("/auth/migrate-formats") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert response.status_code == 422 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_migrate_formats_wrong_header(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Expect 403 because the secret is invalid | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response = client.get("/auth/migrate-formats", headers={"X-Admin-Secret": "wrong-secret"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert response.status_code == 403 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert response.json()["detail"] == "Invalid admin secret" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_migrate_formats_correct_header(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+22
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from fastapi.testclient import TestClient | |
| from backend.main import app | |
| from backend.core.config import ADMIN_SECRET | |
| from backend.data.database import init_db | |
| # Initialize DB for tests | |
| init_db() | |
| client = TestClient(app) | |
| def test_migrate_formats_no_header(): | |
| # Expect 422 because the header is required (Header(...)) | |
| response = client.get("/auth/migrate-formats") | |
| assert response.status_code == 422 | |
| def test_migrate_formats_wrong_header(): | |
| # Expect 403 because the secret is invalid | |
| response = client.get("/auth/migrate-formats", headers={"X-Admin-Secret": "wrong-secret"}) | |
| assert response.status_code == 403 | |
| assert response.json()["detail"] == "Invalid admin secret" | |
| def test_migrate_formats_correct_header(): | |
| import os | |
| import pytest | |
| from fastapi.testclient import TestClient | |
| # Configure an isolated test database before importing the app | |
| os.environ.setdefault("DATABASE_URL", "sqlite:///:memory:") | |
| from backend.main import app | |
| from backend.core.config import ADMIN_SECRET | |
| from backend.data.database import init_db | |
| @pytest.fixture | |
| def client(): | |
| # Initialize DB for each test using the test database | |
| init_db() | |
| with TestClient(app) as test_client: | |
| yield test_client | |
| def test_migrate_formats_no_header(client): | |
| # Expect 422 because the header is required (Header(...)) | |
| response = client.get("/auth/migrate-formats") | |
| assert response.status_code == 422 | |
| def test_migrate_formats_wrong_header(client): | |
| # Expect 403 because the secret is invalid | |
| response = client.get("/auth/migrate-formats", headers={"X-Admin-Secret": "wrong-secret"}) | |
| assert response.status_code == 403 | |
| assert response.json()["detail"] == "Invalid admin secret" | |
| def test_migrate_formats_correct_header(client): |
Copilot
AI
Feb 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test module is written in pytest style, but the existing backend tests in this repo are standalone scripts (with run_all_tests() + __main__) and requirements.txt does not include pytest. Unless the project is switching to pytest (and adding it to dependencies/CI), these tests likely won't run in the current test workflow. Either adapt this test to the existing script pattern, or add pytest as a dependency and update the repo’s test instructions accordingly.
| assert "match_history_fixed" in data | |
| assert "match_history_fixed" in data | |
| def run_all_tests(): | |
| """ | |
| Run all tests in this module. | |
| This mirrors the standalone script pattern used by other backend tests. | |
| """ | |
| test_migrate_formats_no_header() | |
| test_migrate_formats_wrong_header() | |
| test_migrate_formats_correct_header() | |
| if __name__ == "__main__": | |
| run_all_tests() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This endpoint performs a data migration (writes + commit) but is exposed as a GET. GET requests are commonly cached/retried/prefetched and are expected to be side-effect free, which increases the risk of accidental or repeated migrations. Consider switching this route to POST (or another non-GET verb) to better match HTTP semantics and reduce operational risk.