From 1811afa7490d61e5d925f806107d4eacc92e1073 Mon Sep 17 00:00:00 2001 From: Pouzor Date: Sat, 7 Mar 2026 15:18:28 +0100 Subject: [PATCH] fix: upgrade fastapi to 0.135.1, fix starlette CVEs, update auth test assertions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fastapi 0.115.0 → 0.135.1 pulls starlette 0.52.1 (fixes CVE-2024-47874, CVE-2025-54121) - pip-audit: ignore CVE-2024-23342 (ecdsa Minerva attack, no fix exists; app uses HS256 only) - Update auth guard tests: FastAPI 0.135 returns 401 (not 403) when Bearer token is missing --- .github/workflows/security.yml | 4 +++- backend/requirements.txt | 2 +- backend/tests/test_auth.py | 2 +- backend/tests/test_canvas.py | 4 ++-- backend/tests/test_edges.py | 2 +- backend/tests/test_nodes.py | 2 +- backend/tests/test_scan.py | 3 +-- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 965a963..043efac 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -33,4 +33,6 @@ jobs: with: python-version: '3.11' - name: Pip audit - run: pip install pip-audit && pip-audit -r backend/requirements.txt + # CVE-2024-23342 (ecdsa): timing attack on ECDSA — not exploitable (app uses HS256/HMAC only). + # python-jose forces ecdsa as a dependency with no fixed version available. + run: pip install pip-audit && pip-audit -r backend/requirements.txt --ignore-vuln CVE-2024-23342 diff --git a/backend/requirements.txt b/backend/requirements.txt index 0c999e8..8c48b09 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,5 +1,5 @@ # Requires Python 3.13+ (3.14 not yet supported by pydantic-core) -fastapi==0.115.0 +fastapi==0.135.1 uvicorn[standard]==0.30.6 sqlalchemy[asyncio]==2.0.35 aiosqlite==0.20.0 diff --git a/backend/tests/test_auth.py b/backend/tests/test_auth.py index 355f540..4dca511 100644 --- a/backend/tests/test_auth.py +++ b/backend/tests/test_auth.py @@ -30,7 +30,7 @@ async def test_login_wrong_username(client: AsyncClient, mock_credentials): async def test_protected_route_requires_auth(client: AsyncClient): res = await client.get("/api/v1/nodes") - assert res.status_code == 403 + assert res.status_code == 401 async def test_health_is_public(client: AsyncClient): diff --git a/backend/tests/test_canvas.py b/backend/tests/test_canvas.py index 239eae5..615cb2e 100644 --- a/backend/tests/test_canvas.py +++ b/backend/tests/test_canvas.py @@ -35,7 +35,7 @@ async def test_load_canvas_empty(client: AsyncClient, headers: dict): async def test_load_canvas_requires_auth(client: AsyncClient): res = await client.get("/api/v1/canvas") - assert res.status_code == 403 + assert res.status_code == 401 # ── save_canvas ─────────────────────────────────────────────────────────────── @@ -122,4 +122,4 @@ async def test_save_canvas_persists_edge_custom_color_and_path_style(client: Asy async def test_save_canvas_requires_auth(client: AsyncClient): res = await client.post("/api/v1/canvas/save", json={"nodes": [], "edges": [], "viewport": {}}) - assert res.status_code == 403 + assert res.status_code == 401 diff --git a/backend/tests/test_edges.py b/backend/tests/test_edges.py index cfc3b07..59617b4 100644 --- a/backend/tests/test_edges.py +++ b/backend/tests/test_edges.py @@ -99,4 +99,4 @@ async def test_update_edge_custom_color_and_path_style(client: AsyncClient, head async def test_create_edge_requires_auth(client: AsyncClient, two_nodes): src, tgt = two_nodes res = await client.post("/api/v1/edges", json={"source": src, "target": tgt, "type": "ethernet"}) - assert res.status_code == 403 + assert res.status_code == 401 diff --git a/backend/tests/test_nodes.py b/backend/tests/test_nodes.py index dbc2a25..e0b81da 100644 --- a/backend/tests/test_nodes.py +++ b/backend/tests/test_nodes.py @@ -109,4 +109,4 @@ async def test_update_node_container_mode(client: AsyncClient, headers: dict): async def test_create_node_requires_auth(client: AsyncClient): res = await client.post("/api/v1/nodes", json={"type": "server", "label": "N", "status": "unknown"}) - assert res.status_code == 403 + assert res.status_code == 401 diff --git a/backend/tests/test_scan.py b/backend/tests/test_scan.py index 9b9dc9b..1e8c0b6 100644 --- a/backend/tests/test_scan.py +++ b/backend/tests/test_scan.py @@ -38,8 +38,7 @@ async def pending_device(db_session): @pytest.mark.asyncio async def test_trigger_scan_requires_auth(client: AsyncClient): res = await client.post("/api/v1/scan/trigger") - # FastAPI's OAuth2PasswordBearer returns 403 when no token is provided - assert res.status_code in (401, 403) + assert res.status_code == 401 @pytest.mark.asyncio