diff --git a/api/main.py b/api/main.py index 69e26e6..5325a48 100644 --- a/api/main.py +++ b/api/main.py @@ -105,21 +105,6 @@ async def lifespan(_app: FastAPI): max_age=100, ) -# Never log these header values verbatim (tokens/session identifiers): -_SENSITIVE_LOG_HEADERS = frozenset({"authorization", "cookie", "set-cookie"}) - - -# Registered after CORSMiddleware, so it runs outermost and sees every -# request -- including OPTIONS preflights CORSMiddleware intercepts itself. -@app.middleware("http") -async def log_request_headers(request: Request, call_next): - safe_headers = { - k: ("" if k.lower() in _SENSITIVE_LOG_HEADERS else v) - for k, v in request.headers.items() - } - logger.info(f"{request.method} {request.url.path} headers={safe_headers}") - return await call_next(request) - # Include routers app.include_router(osm_router, prefix="/api/v1") @@ -196,19 +181,6 @@ def get_workspace_repository( "forwarded", } -# osm-rails sets its own Access-Control-*/Vary headers on some API responses -# (e.g. GET /api/0.6/users) for direct browser access. Forwarding those -# verbatim alongside our own CORSMiddleware's headers produces duplicate, -# conflicting values that browsers reject as a CORS error. -STRIP_RESPONSE_HEADERS = HOP_BY_HOP_HEADERS | { - "vary", - "access-control-allow-origin", - "access-control-allow-credentials", - "access-control-allow-methods", - "access-control-allow-headers", - "access-control-expose-headers", - "access-control-max-age", -} # Paths that do not require X-Workspace header, scoped by HTTP method. Each # entry is a tuple of: (compiled regex, set of allowed methods). @@ -258,9 +230,7 @@ async def capabilities(request: Request): ) forwarded_headers = { - k: v - for k, v in rp_resp.headers.items() - if k.lower() not in STRIP_RESPONSE_HEADERS + k: v for k, v in rp_resp.headers.items() if k.lower() not in HOP_BY_HOP_HEADERS } return StreamingResponse( @@ -375,7 +345,6 @@ async def catch_all( ) try: rp_resp = await client.send(rp_req, stream=True) - logger.info(f"Upstream request to {rp_req.url} sent successfully") except httpx.TimeoutException: raise HTTPException( status_code=status.HTTP_504_GATEWAY_TIMEOUT, @@ -396,9 +365,7 @@ async def catch_all( logger.warning(msg) forwarded_headers = { - k: v - for k, v in rp_resp.headers.items() - if k.lower() not in STRIP_RESPONSE_HEADERS + k: v for k, v in rp_resp.headers.items() if k.lower() not in HOP_BY_HOP_HEADERS } return StreamingResponse( diff --git a/tests/integration/test_proxy.py b/tests/integration/test_proxy.py index f1a43e0..6cd818d 100644 --- a/tests/integration/test_proxy.py +++ b/tests/integration/test_proxy.py @@ -156,31 +156,6 @@ async def test_hop_by_hop_response_headers_are_stripped(client, login, monkeypat assert "keep-alive" not in response.headers -async def test_upstream_cors_headers_are_stripped(client, login, monkeypatch): - # osm-rails sets its own Access-Control-*/Vary on some responses; forwarding - # them would duplicate/conflict with CORSMiddleware's own headers. - install_osm( - monkeypatch, - lambda req: ( - 200, - { - "content-type": "application/xml", - "access-control-allow-origin": "*", - "vary": "Origin", - }, - b"", - ), - ) - login(factories.make_user_info(accessible_workspace_ids={"pg": [1]})) - - response = await client.get("/api/0.6/users", headers={"X-Workspace": "1"}) - - assert response.status_code == 200 - assert "vary" not in response.headers - # Only CORSMiddleware's Access-Control-Allow-Origin should be present. - assert response.headers.get("access-control-allow-origin") != "*" - - # --- upstream status + body fidelity ---------------------------------------