fix(vertex-passthrough): derive credential-header drop set from SpecialHeaders

The hand-rolled drop set missed Ocp-Apim-Subscription-Key, so a caller
Azure APIM secret in that header was forwarded to Google on the
credential-less branch. Derive the name-drop set from the canonical
SpecialHeaders.litellm_credential_header_names(), minus Authorization and
x-goog-api-key which double as real Google credentials and are value-stripped
instead. New credential headers added there are now dropped automatically.
This commit is contained in:
mateo-berri 2026-08-24 12:45:41 -07:00
parent ee0363249d
commit ab93636e2c
2 changed files with 32 additions and 6 deletions

View file

@ -1742,8 +1742,9 @@ def _bearer_stripped(value: str) -> str:
return value
_HEADERS_NEVER_FORWARDED_TO_VERTEX: Final = frozenset(
{"content-length", "host", "x-litellm-api-key", "api-key", "x-api-key"}
_VERTEX_UPSTREAM_CREDENTIAL_HEADERS: Final = frozenset({"authorization", "x-goog-api-key"})
_HEADERS_NEVER_FORWARDED_TO_VERTEX: Final = frozenset({"content-length", "host"}) | (
SpecialHeaders.litellm_credential_header_names() - _VERTEX_UPSTREAM_CREDENTIAL_HEADERS
)
@ -1772,9 +1773,12 @@ def _forwarded_headers_for_credentialless_vertex_passthrough(request: Request) -
branch, used when the proxy has no Vertex credential configured.
No credential the proxy accepts for caller authentication is forwarded to
Google. Vertex only ever authenticates with an OAuth token in ``Authorization``
or an API key in ``x-goog-api-key``, so the proxy-only auth headers Google never
consumes (``x-litellm-api-key`` / ``api-key`` / ``x-api-key``) are dropped by
Google. ``user_api_key_auth`` reads the caller's key from every header in
``SpecialHeaders.litellm_credential_header_names()``, and Vertex only ever
authenticates with an OAuth token in ``Authorization`` or an API key in
``x-goog-api-key``. So the proxy-only auth headers Google never consumes
(everything in that set except those two, e.g. ``x-litellm-api-key`` /
``api-key`` / ``x-api-key`` / ``Ocp-Apim-Subscription-Key``) are dropped by
name. ``Authorization`` and ``x-goog-api-key`` may instead carry a genuine
bring-your-own Google credential, so they are kept unless their value is one of
the caller's LiteLLM key values, which are dropped by value (normalizing any

View file

@ -35,7 +35,7 @@ from litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints import (
vertex_proxy_route,
vllm_proxy_route,
)
from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth
from litellm.proxy._types import LitellmUserRoles, SpecialHeaders, UserAPIKeyAuth
from litellm.types.passthrough_endpoints.vertex_ai import VertexPassThroughCredentials
@ -3608,6 +3608,28 @@ class TestVertexCredentiallessPassthroughVirtualKeyLeak:
assert "azure-style-caller-secret" not in forwarded_blob
assert "anthropic-style-caller-secret" not in forwarded_blob
@pytest.mark.asyncio
@pytest.mark.parametrize(
"credential_header",
sorted(SpecialHeaders.litellm_credential_header_names() - {"authorization", "x-goog-api-key"}),
)
async def test_every_non_google_credential_header_is_dropped_by_name(self, monkeypatch, credential_header):
raised, forwarded = await self._run(
monkeypatch,
[
(b"x-goog-api-key", b"AIza-real-google-api-key"),
(credential_header.encode(), b"some-distinct-caller-secret-value"),
(b"content-type", b"application/json"),
],
)
assert raised is None
assert forwarded is not None
assert forwarded.get("x-goog-api-key") == "AIza-real-google-api-key"
assert credential_header not in forwarded
assert "some-distinct-caller-secret-value" not in " ".join(
f"{name}:{value}" for name, value in forwarded.items()
)
@pytest.mark.asyncio
async def test_virtual_key_in_operator_configured_header_is_stripped(self, monkeypatch):
with mock.patch.dict( # test-quality-ok: general_settings is the real proxy config surface for litellm_key_header_name; no injection seam exists on this route