fix(credentials): mask OTEL headers for lower roles
Some checks failed
LiteLLM Rust / rustfmt, clippy, test (push) Has been cancelled

This commit is contained in:
Devin AI 2026-07-19 03:10:28 +00:00
parent 58cd50b512
commit b55f45bd38
2 changed files with 62 additions and 3 deletions

View file

@ -306,7 +306,8 @@ async def get_credentials(
from litellm.proxy.proxy_server import prisma_client
try:
if _is_proxy_admin(user_api_key_dict):
is_proxy_admin = _is_proxy_admin(user_api_key_dict)
if is_proxy_admin:
visible = list(litellm.credential_list)
else:
scope = await _caller_admin_scope(user_api_key_dict, prisma_client)
@ -331,7 +332,14 @@ async def get_credentials(
masked_credentials = [
{
"credential_name": credential.credential_name,
"credential_values": _get_masked_values(credential.credential_values),
"credential_values": (
{
**_get_masked_values(credential.credential_values),
"otel_headers": "********",
}
if not is_proxy_admin and "otel_headers" in credential.credential_values
else _get_masked_values(credential.credential_values)
),
"credential_info": credential.credential_info,
}
for credential in visible

View file

@ -617,6 +617,44 @@ async def test_get_credentials_shows_only_in_scope_destinations_for_non_admin(
assert names == ["poc-langfuse"]
@pytest.mark.asyncio
async def test_get_credentials_masks_otel_headers_only_for_non_admin(
monkeypatch, _patch_team_admin_lookup
):
raw_headers = "Authorization=Bearer collector-secret,x-api-key=api-secret"
monkeypatch.setattr(
litellm,
"credential_list",
[
CredentialItem(
credential_name="generic-otel",
credential_values={
"otel_endpoint": "https://collector.example.com/v1/traces",
"otel_headers": raw_headers,
},
credential_info={
"credential_type": "logging",
"description": "generic",
"access": {"teams": ["team-existing"]},
},
),
],
)
_patch_team_admin_lookup["ids"] = frozenset({"team-existing"})
response = await endpoints.get_credentials(
request=MagicMock(),
fastapi_response=MagicMock(),
user_api_key_dict=_team_admin_of(["team-existing"]),
)
values = response["credentials"][0]["credential_values"]
assert values == {
"otel_endpoint": "https://collector.example.com/v1/traces",
"otel_headers": "********",
}
@pytest.mark.asyncio
async def test_get_credentials_hides_out_of_scope_destination(
monkeypatch, _patch_team_admin_lookup
@ -822,6 +860,7 @@ async def test_patch_credentials_echoes_foreign_team_id_to_legit_team_admin(
@pytest.mark.asyncio
async def test_get_credentials_returns_all_for_proxy_admin(monkeypatch):
raw_headers = "Authorization=Bearer collector-secret,x-api-key=api-secret"
monkeypatch.setattr(
litellm,
"credential_list",
@ -836,6 +875,14 @@ async def test_get_credentials_returns_all_for_proxy_admin(monkeypatch):
credential_values={"public_key": "pk-1"},
credential_info=_DEST_WITH_TEAMS,
),
CredentialItem(
credential_name="generic-otel",
credential_values={"otel_headers": raw_headers},
credential_info={
"credential_type": "logging",
"description": "generic",
},
),
],
)
response = await endpoints.get_credentials(
@ -844,7 +891,11 @@ async def test_get_credentials_returns_all_for_proxy_admin(monkeypatch):
user_api_key_dict=_admin(),
)
names = sorted(c["credential_name"] for c in response["credentials"])
assert names == ["openai", "poc-langfuse"]
assert names == ["generic-otel", "openai", "poc-langfuse"]
generic = next(
c for c in response["credentials"] if c["credential_name"] == "generic-otel"
)
assert generic["credential_values"]["otel_headers"] == raw_headers
@pytest.mark.asyncio