From c18ad848fff9777417e590261d389f79d423b073 Mon Sep 17 00:00:00 2001 From: user <70670632+stuxf@users.noreply.github.com> Date: Sun, 31 May 2026 00:42:18 +0000 Subject: [PATCH] chore(proxy): redact query-string secrets in URL-valued callback vars A URL-valued callback var (e.g. GENERIC_LOGGER_ENDPOINT) can carry a token as a query param, which userinfo-only redaction missed. Apply mask_url_query_values alongside mask_url_credentials on the masked /get/config/callbacks path. --- litellm/proxy/common_utils/callback_utils.py | 8 +++++++- .../proxy/common_utils/test_callback_utils.py | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/common_utils/callback_utils.py b/litellm/proxy/common_utils/callback_utils.py index 44ed1d9c0ae..e854e16cc5c 100644 --- a/litellm/proxy/common_utils/callback_utils.py +++ b/litellm/proxy/common_utils/callback_utils.py @@ -9,6 +9,7 @@ from litellm.litellm_core_utils.sensitive_data_masker import ( SensitiveDataMasker, mask_sensitive_keys, mask_url_credentials, + mask_url_query_values, ) from litellm.proxy._types import CommonProxyErrors, LiteLLMPromptInjectionParams from litellm.proxy.common_utils.encrypt_decrypt_utils import ( @@ -652,7 +653,12 @@ def process_callback( if _is_sensitive_callback_var(k) or k.upper().endswith("_HEADERS") } env_vars_dict = mask_sensitive_keys(env_vars_dict, sensitive_keys) - env_vars_dict = {k: mask_url_credentials(v) for k, v in env_vars_dict.items()} + # URL-valued callback vars (e.g. GENERIC_LOGGER_ENDPOINT) can embed a + # token in userinfo or as a query param, so redact both. + env_vars_dict = { + k: mask_url_query_values(mask_url_credentials(v)) + for k, v in env_vars_dict.items() + } return {"name": _callback, "variables": env_vars_dict, "type": callback_type} diff --git a/tests/test_litellm/proxy/common_utils/test_callback_utils.py b/tests/test_litellm/proxy/common_utils/test_callback_utils.py index d101f5e8a9d..baac2dde3ef 100644 --- a/tests/test_litellm/proxy/common_utils/test_callback_utils.py +++ b/tests/test_litellm/proxy/common_utils/test_callback_utils.py @@ -163,6 +163,24 @@ def test_process_callback_masks_headers_vars(mock_get_env_vars): assert result["variables"]["OTEL_ENDPOINT"] == "http://collector.internal:4317" +@patch( + "litellm.proxy.common_utils.callback_utils.CustomLogger.get_callback_env_vars", + return_value=["GENERIC_LOGGER_ENDPOINT"], +) +def test_process_callback_masks_url_query_secrets(mock_get_env_vars): + """A URL-valued callback var can carry a token as a query param; it is + redacted even though the var name is not credential-like.""" + result = process_callback( + _callback="generic", + callback_type="success", + environment_variables={ + "GENERIC_LOGGER_ENDPOINT": "https://logs.example.com/ingest?token=qp-secret-token" + }, + mask_sensitive=True, + ) + assert "qp-secret-token" not in result["variables"]["GENERIC_LOGGER_ENDPOINT"] + + def test_normalize_callback_names_none_returns_empty_list(): assert normalize_callback_names(None) == [] assert normalize_callback_names([]) == []