From 088a700933cf77eb6c33796da0cd2d9732a345ec Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 24 Aug 2026 12:00:21 -0700 Subject: [PATCH] test(passthrough): cover virtual key echoed in api-key and x-api-key Adds a regression asserting the value-based strip also drops the caller's virtual key when it is duplicated into the api-key and x-api-key headers, while a genuine bring-your-own Google credential still forwards. --- .../test_llm_pass_through_endpoints.py | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py b/tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py index fe0d1a65c70..52ff6e709c5 100644 --- a/tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py +++ b/tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py @@ -3450,14 +3450,16 @@ class TestVertexCredentiallessPassthroughVirtualKeyLeak: With no Vertex credential configured, the passthrough took the bring-your-own-credentials branch and forwarded the whole incoming header set - to Google, including whichever header carried the caller's LiteLLM virtual key - (``Authorization: Bearer `` or ``x-litellm-api-key: ``). That leaked - the proxy's own secret to an upstream provider. + to Google, including whichever header carried the caller's LiteLLM virtual key. + LiteLLM accepts that key from several headers (``Authorization``, + ``x-litellm-api-key``, ``x-goog-api-key``, ``api-key``, ``x-api-key``), and + ``x-goog-api-key`` doubles as a genuine Google credential, so any of them could + leak the proxy's own secret to an upstream provider. A credential-less request that carries no upstream Google credential must now fail with a clean 401 and never reach ``create_pass_through_route``; a genuine bring-your-own Google credential must still pass through, with the virtual key - stripped from what is forwarded. + stripped by value from every forwarded header. """ VKEY = "sk-litellm-victim-key" @@ -3579,6 +3581,26 @@ class TestVertexCredentiallessPassthroughVirtualKeyLeak: assert "x-litellm-api-key" not in forwarded assert self.VKEY not in " ".join(f"{name}:{value}" for name, value in forwarded.items()) + @pytest.mark.asyncio + async def test_virtual_key_echoed_in_alternate_auth_headers_is_stripped_by_value(self, monkeypatch): + raised, forwarded = await self._run( + monkeypatch, + [ + (b"x-litellm-api-key", self.VKEY.encode()), + (b"authorization", b"Bearer ya29.google-oauth-token"), + (b"api-key", self.VKEY.encode()), + (b"x-api-key", self.VKEY.encode()), + (b"content-type", b"application/json"), + ], + ) + assert raised is None + assert forwarded is not None + assert forwarded.get("authorization") == "Bearer ya29.google-oauth-token" + assert "api-key" not in forwarded + assert "x-api-key" not in forwarded + assert "x-litellm-api-key" not in forwarded + assert self.VKEY not in " ".join(f"{name}:{value}" for name, value in forwarded.items()) + class TestGetAzureAISearchIndexFromEndpoint: """The operable index is only the segment right after ``indexes``.