From fea402c580a57273a19d933ce3b4733fbb75d1d5 Mon Sep 17 00:00:00 2001 From: user <70670632+stuxf@users.noreply.github.com> Date: Fri, 24 Apr 2026 18:44:57 +0000 Subject: [PATCH] fix(mcp): fail closed on DB outage in BYOK credential check `_check_byok_credential` previously returned silently when `prisma_client` was None, bypassing BYOK ownership validation during database-outage windows. Any proxy-authenticated user could invoke BYOK-protected MCP tools without a stored credential during the outage window. Now raises HTTP 503 with a structured error so the flow fails closed. Regression test asserts 503 is raised when `prisma_client` is None. Reported by @brodmart in GHSA-6762-2m23-5mxp. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../proxy/_experimental/mcp_server/server.py | 13 ++++++++- .../mcp_server/test_byok_oauth_endpoints.py | 29 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index 5f320655931..c2e998f01e5 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -1952,7 +1952,18 @@ if MCP_AVAILABLE: from litellm.proxy.proxy_server import prisma_client if prisma_client is None: - return + # Fail closed on DB unavailability: returning here previously + # bypassed the ownership check and let any proxy-authenticated + # caller invoke BYOK tools during outage windows. + raise HTTPException( + status_code=503, + detail={ + "error": "byok_auth_unavailable", + "server_id": mcp_server.server_id, + "server_name": mcp_server.server_name or mcp_server.name, + "message": "BYOK credential check requires a database connection.", + }, + ) credential = await get_user_credential( prisma_client=prisma_client, diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_byok_oauth_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_byok_oauth_endpoints.py index 48dfc1715f7..89992c510f5 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_byok_oauth_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_byok_oauth_endpoints.py @@ -564,6 +564,35 @@ async def test_check_byok_credential_has_credential(): await _check_byok_credential(server, user_auth) +@pytest.mark.asyncio +async def test_check_byok_credential_db_unavailable_fails_closed(): + """BYOK server with no prisma_client → 503, not silent pass. + + Regression for GHSA-6762: previously returned silently, bypassing the + ownership check during DB outage windows. + """ + from litellm.proxy._experimental.mcp_server.server import _check_byok_credential + from litellm.proxy._types import UserAPIKeyAuth + from litellm.types.mcp_server.mcp_server_manager import MCPServer + + server = MCPServer( + server_id="byok-4", + name="byok-server", + transport=MCPTransport.http, + is_byok=True, + ) + user_auth = UserAPIKeyAuth(user_id="user-55", api_key="sk-test") + + with patch("litellm.proxy.proxy_server.prisma_client", None): + with pytest.raises(HTTPException) as exc_info: + await _check_byok_credential(server, user_auth) + + assert exc_info.value.status_code == 503 + detail: Any = exc_info.value.detail + assert detail["error"] == "byok_auth_unavailable" + assert detail["server_id"] == "byok-4" + + # --------------------------------------------------------------------------- # Security regression tests for AO2kf_-9 / GHSA-jg3h: # Unauthenticated /v1/mcp/oauth/authorize previously allowed an attacker to