mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-25 01:02:15 +00:00
fix(proxy): never render credential-bearing config keys in the bug report (#42493)
Co-authored-by: ryan <ryan@berri.ai> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
f3c920a06b
commit
f275be5fac
2 changed files with 78 additions and 6 deletions
|
|
@ -21,6 +21,30 @@ from litellm.types.secret_managers.main import KeyManagementSystem
|
|||
_OBJECT_MAP: Final[TypeAdapter[Mapping[str, object]]] = TypeAdapter(Mapping[str, object])
|
||||
_OBJECT_LIST: Final[TypeAdapter[tuple[object, ...]]] = TypeAdapter(tuple[object, ...])
|
||||
_JSON: Final[TypeAdapter[JsonValue]] = TypeAdapter(JsonValue)
|
||||
CREDENTIAL_KEY_PARTS: Final = frozenset(
|
||||
{
|
||||
"key",
|
||||
"keys",
|
||||
"secret",
|
||||
"secrets",
|
||||
"token",
|
||||
"password",
|
||||
"passwd",
|
||||
"credential",
|
||||
"credentials",
|
||||
"url",
|
||||
"uri",
|
||||
"dsn",
|
||||
"host",
|
||||
"hosts",
|
||||
"base",
|
||||
"endpoint",
|
||||
"cert",
|
||||
"pem",
|
||||
"salt",
|
||||
}
|
||||
)
|
||||
ENUM_KEYS_WITH_CREDENTIAL_PARTS: Final = frozenset({"key_management_system"})
|
||||
|
||||
|
||||
def _object_map(value: object) -> Mapping[str, object]:
|
||||
|
|
@ -87,22 +111,26 @@ def _cache_params_keys() -> frozenset[str]:
|
|||
return frozenset(name for name in inspect.signature(Cache.__init__).parameters if name != "self") # pyright: ignore[reportUnknownMemberType, reportUnknownArgumentType] # untyped params, only names are read
|
||||
|
||||
|
||||
def _render_json(value: JsonValue) -> str | None:
|
||||
def _is_credential_key(key: str) -> bool:
|
||||
return key not in ENUM_KEYS_WITH_CREDENTIAL_PARTS and not CREDENTIAL_KEY_PARTS.isdisjoint(key.lower().split("_"))
|
||||
|
||||
|
||||
def _render_json(key: str, value: JsonValue) -> str | None:
|
||||
match value:
|
||||
case bool():
|
||||
return str(value).lower()
|
||||
case str():
|
||||
return value if value in _known_values() else None
|
||||
return value if value in _known_values() and not _is_credential_key(key) else None
|
||||
case list():
|
||||
known_items: Final = tuple(rendered for item in value if (rendered := _render_json(item)) is not None)
|
||||
known_items: Final = tuple(rendered for item in value if (rendered := _render_json(key, item)) is not None)
|
||||
return f"[{', '.join(known_items)}]" if known_items else None
|
||||
case _:
|
||||
return None
|
||||
|
||||
|
||||
def _render(value: object) -> str | None:
|
||||
def _render(key: str, value: object) -> str | None:
|
||||
try:
|
||||
return _render_json(_JSON.validate_python(value))
|
||||
return _render_json(key, _JSON.validate_python(value))
|
||||
except ValidationError:
|
||||
return None
|
||||
|
||||
|
|
@ -111,7 +139,7 @@ def _section_lines(section: str, values: Mapping[str, object], known_keys: froze
|
|||
return tuple(
|
||||
f"{section}.{key} = {rendered}"
|
||||
for key, value in values.items()
|
||||
if key in known_keys and (rendered := _render(value)) is not None
|
||||
if key in known_keys and (rendered := _render(key, value)) is not None
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,50 @@ def test_secrets_numbers_and_unknown_values_leave_no_line():
|
|||
assert safe_config_lines({}, general_settings) == ("general_settings.background_health_checks = false",)
|
||||
|
||||
|
||||
def test_credential_keys_never_render_even_when_the_secret_equals_a_litellm_token():
|
||||
config: Mapping[str, object] = {
|
||||
"litellm_settings": {
|
||||
"openai_key": "openai",
|
||||
"token": "langfuse",
|
||||
"api_base": "azure",
|
||||
"callbacks": ["langfuse"],
|
||||
"cache_params": {"type": "redis", "password": "redis", "host": "openai", "qdrant_api_key": "qdrant"},
|
||||
},
|
||||
"router_settings": {
|
||||
"routing_strategy": "simple-shuffle",
|
||||
"redis_password": "simple-shuffle",
|
||||
"redis_url": "redis",
|
||||
},
|
||||
"guardrails": [
|
||||
{
|
||||
"litellm_params": {
|
||||
"guardrail": "presidio",
|
||||
"api_key": "presidio",
|
||||
"auth_token": "pre_call",
|
||||
"client_secret": "openai",
|
||||
"credentials": ["openai", "azure"],
|
||||
}
|
||||
}
|
||||
],
|
||||
}
|
||||
general_settings: Mapping[str, object] = {
|
||||
"master_key": "redis",
|
||||
"database_url": "openai",
|
||||
"alert_to_webhook_url": "langfuse",
|
||||
"key_management_system": "aws_secret_manager",
|
||||
"use_azure_key_vault": True,
|
||||
}
|
||||
|
||||
assert safe_config_lines(config, general_settings) == (
|
||||
"general_settings.key_management_system = aws_secret_manager",
|
||||
"general_settings.use_azure_key_vault = true",
|
||||
"litellm_settings.callbacks = [langfuse]",
|
||||
"litellm_settings.cache_params.type = redis",
|
||||
"router_settings.routing_strategy = simple-shuffle",
|
||||
"guardrails[0].litellm_params.guardrail = presidio",
|
||||
)
|
||||
|
||||
|
||||
def test_malformed_sections_produce_no_lines():
|
||||
config: Mapping[str, object] = {
|
||||
"litellm_settings": "acme",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue