removed jwt tokens from detailed_debug logging

This commit is contained in:
shivam 2026-02-12 17:39:08 -08:00
parent af3acdda18
commit 82204d3e97
3 changed files with 85 additions and 7 deletions

View file

@ -252,6 +252,34 @@ def clean_headers(
return clean_headers
def _mask_secret_fields_for_logging(secret_fields: dict) -> dict:
"""
Masks sensitive values (like JWT tokens) in secret_fields before logging.
Keeps the field structure intact but redacts authorization header values.
"""
_sensitive_header_keys = frozenset(
{"authorization", "x-api-key", "api-key", "x-litellm-api-key"}
)
if not isinstance(secret_fields, dict):
return secret_fields
masked = {}
for field_key, field_value in secret_fields.items():
if isinstance(field_value, dict):
masked_inner = {}
for k, v in field_value.items():
if k.lower() in _sensitive_header_keys and isinstance(v, str):
if len(v) > 20:
masked_inner[k] = v[:10] + "****" + v[-4:]
else:
masked_inner[k] = "****"
else:
masked_inner[k] = v
masked[field_key] = masked_inner
else:
masked[field_key] = field_value
return masked
class LiteLLMProxyRequestSetup:
@staticmethod
def _get_timeout_from_request(headers: dict) -> Optional[float]:
@ -899,13 +927,19 @@ async def add_litellm_data_to_request( # noqa: PLR0915
## Cache Controls
headers = request.headers
verbose_proxy_logger.debug("Request Headers: %s", headers)
verbose_proxy_logger.debug("Request Headers: %s", clean_headers(headers))
cache_control_header = headers.get("Cache-Control", None)
if cache_control_header:
cache_dict = parse_cache_control(cache_control_header)
data["ttl"] = cache_dict.get("s-maxage")
verbose_proxy_logger.debug("receiving data: %s", data)
verbose_proxy_logger.debug(
"receiving data: %s",
{
k: (_mask_secret_fields_for_logging(v) if k == "secret_fields" else v)
for k, v in data.items()
},
)
# Parse metadata if it's a string (e.g., from multipart/form-data)
if "metadata" in data and data["metadata"] is not None:
@ -1133,7 +1167,11 @@ async def add_litellm_data_to_request( # noqa: PLR0915
)
verbose_proxy_logger.debug(
"[PROXY] returned data from litellm_pre_call_utils: %s", data
"[PROXY] returned data from litellm_pre_call_utils: %s",
{
k: (_mask_secret_fields_for_logging(v) if k == "secret_fields" else v)
for k, v in data.items()
},
)
## ENFORCED PARAMS CHECK

View file

@ -328,7 +328,11 @@ from litellm.proxy.hooks.prompt_injection_detection import (
)
from litellm.proxy.hooks.proxy_track_cost_callback import _ProxyDBLogger
from litellm.proxy.image_endpoints.endpoints import router as image_router
from litellm.proxy.litellm_pre_call_utils import add_litellm_data_to_request
from litellm.proxy.litellm_pre_call_utils import (
_mask_secret_fields_for_logging,
add_litellm_data_to_request,
clean_headers,
)
from litellm.proxy.management_endpoints.budget_management_endpoints import (
router as budget_management_router,
)
@ -9838,11 +9842,17 @@ async def async_queue_request(
data["proxy_server_request"] = {
"url": str(request.url),
"method": request.method,
"headers": dict(request.headers),
"headers": clean_headers(request.headers),
"body": copy.copy(data), # use copy instead of deepcopy
}
verbose_proxy_logger.debug("receiving data: %s", data)
verbose_proxy_logger.debug(
"receiving data: %s",
{
k: (_mask_secret_fields_for_logging(v) if k == "secret_fields" else v)
for k, v in data.items()
},
)
data["model"] = (
general_settings.get("completion_model", None) # server default
or user_model # model name passed via cli args

View file

@ -7267,6 +7267,32 @@ def get_valid_models(
return [] # NON-Blocking
def _mask_secret_fields_for_logging(secret_fields: dict) -> dict:
"""
Masks sensitive values (like JWT tokens) in secret_fields before logging.
Keeps the field structure intact but redacts authorization header values.
"""
if not isinstance(secret_fields, dict):
return secret_fields
masked = {}
for field_key, field_value in secret_fields.items():
if isinstance(field_value, dict):
masked_inner = {}
for k, v in field_value.items():
if k.lower() in ("authorization", "x-api-key", "api-key", "x-litellm-api-key") and isinstance(v, str):
# Show first 10 and last 4 chars, mask the rest
if len(v) > 20:
masked_inner[k] = v[:10] + "****" + v[-4:]
else:
masked_inner[k] = "****"
else:
masked_inner[k] = v
masked[field_key] = masked_inner
else:
masked[field_key] = field_value
return masked
def print_args_passed_to_litellm(original_function, args, kwargs):
if not _is_debugging_on():
return
@ -7292,7 +7318,11 @@ def print_args_passed_to_litellm(original_function, args, kwargs):
return
args_str = ", ".join(map(repr, args))
kwargs_str = ", ".join(f"{key}={repr(value)}" for key, value in kwargs.items())
# Mask JWT tokens / auth headers in secret_fields before logging
kwargs_str = ", ".join(
f"{key}={repr(_mask_secret_fields_for_logging(value)) if key == 'secret_fields' else repr(value)}"
for key, value in kwargs.items()
)
print_verbose(
"\n",
) # new line before