diff --git a/litellm/proxy/common_request_processing.py b/litellm/proxy/common_request_processing.py index cb2b08199b9..a1d102a731f 100644 --- a/litellm/proxy/common_request_processing.py +++ b/litellm/proxy/common_request_processing.py @@ -433,6 +433,13 @@ def _has_attribute_error_in_chain(exc: Exception) -> bool: return False +def _sanitize_for_log(message: str) -> str: + """Strip line breaks from strings before logging to reduce log injection risk.""" + if not isinstance(message, str): + message = str(message) + return message.replace("\r", "").replace("\n", " ") + + class ProxyBaseLLMRequestProcessing: def __init__(self, data: dict): self.data = data @@ -879,9 +886,10 @@ class ProxyBaseLLMRequestProcessing: type(self.data).__name__, ) else: + _safe_payload_str = _sanitize_for_log(_payload_str) verbose_proxy_logger.debug( - "Request received by LiteLLM:\n%s", - _payload_str, + "Request received by LiteLLM: %s", + _safe_payload_str, ) async def base_process_llm_request( # noqa: PLR0915 diff --git a/tests/test_litellm/proxy/test_common_request_processing.py b/tests/test_litellm/proxy/test_common_request_processing.py index c00a6fba58c..03d3a76dc59 100644 --- a/tests/test_litellm/proxy/test_common_request_processing.py +++ b/tests/test_litellm/proxy/test_common_request_processing.py @@ -19,12 +19,21 @@ from litellm.proxy.common_request_processing import ( _is_azure_model_router_request, _override_openai_response_model, _parse_event_data_for_error, + _sanitize_for_log, create_response, ) from litellm.proxy.dd_span_tagger import DDSpanTagger from litellm.proxy.utils import ProxyLogging +class TestSanitizeForLog: + def test_replaces_newlines_and_strips_carriage_returns(self): + assert _sanitize_for_log("x\ny\rz") == "x yz" + + def test_non_string_input_is_stringified(self): + assert _sanitize_for_log(42) == "42" + + class TestProxyBaseLLMRequestProcessing: @pytest.mark.asyncio async def test_common_processing_pre_call_logic_pre_call_hook_receives_litellm_call_id(