mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
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:
parent
ee0363249d
commit
ab93636e2c
2 changed files with 32 additions and 6 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue