mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-12 23:01:41 +00:00
fix(security): redact error_message from spend logs when prompt logging is disabled
This commit is contained in:
parent
72a461ba4a
commit
baef9f1574
2 changed files with 69 additions and 0 deletions
|
|
@ -137,6 +137,17 @@ def _get_spend_logs_metadata(
|
|||
clean_metadata["litellm_overhead_time_ms"] = litellm_overhead_time_ms
|
||||
clean_metadata["cost_breakdown"] = cost_breakdown
|
||||
|
||||
# Redact error_message when prompt/response storage is disabled.
|
||||
# str(exception) for provider errors (e.g. BadRequestError) includes the raw
|
||||
# response body (Received={...}), which violates the no-content-logging policy.
|
||||
if not _should_store_prompts_and_responses_in_spend_logs():
|
||||
error_info = clean_metadata.get("error_information")
|
||||
if isinstance(error_info, dict) and error_info.get("error_message"):
|
||||
clean_metadata["error_information"] = {
|
||||
**error_info,
|
||||
"error_message": None,
|
||||
}
|
||||
|
||||
return clean_metadata
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1469,3 +1469,61 @@ class TestIsMasterKey:
|
|||
master = "sk-master-key-123"
|
||||
hashed = hash_token(master)
|
||||
assert _is_master_key(api_key=hashed, _master_key=master) is True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# error_message redaction in spend logs
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
STORE_PROMPTS_PATH = "litellm.proxy.spend_tracking.spend_tracking_utils._should_store_prompts_and_responses_in_spend_logs"
|
||||
|
||||
|
||||
def _failure_metadata_with_error(error_msg: str) -> dict:
|
||||
return {
|
||||
"user_api_key": "sk-test",
|
||||
"status": "failure",
|
||||
"error_information": {
|
||||
"error_code": "400",
|
||||
"error_class": "BadRequestError",
|
||||
"llm_provider": "vertex_ai",
|
||||
"traceback": "Traceback ...",
|
||||
"error_message": error_msg,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@patch(STORE_PROMPTS_PATH, return_value=False)
|
||||
def test_error_message_redacted_when_store_prompts_disabled(mock_store):
|
||||
"""error_message must be None when store_prompts_in_spend_logs is False."""
|
||||
raw = 'litellm.BadRequestError: Received={"candidates": [{"content": {"role": "model", "parts": [{"text": "secret content"}]}}]}'
|
||||
result = _get_spend_logs_metadata(_failure_metadata_with_error(raw))
|
||||
assert result["error_information"]["error_message"] is None
|
||||
|
||||
|
||||
@patch(STORE_PROMPTS_PATH, return_value=True)
|
||||
def test_error_message_kept_when_store_prompts_enabled(mock_store):
|
||||
"""error_message must be preserved when store_prompts_in_spend_logs is True."""
|
||||
raw = 'litellm.BadRequestError: Received={"candidates": [{"content": {"role": "model"}}]}'
|
||||
result = _get_spend_logs_metadata(_failure_metadata_with_error(raw))
|
||||
assert result["error_information"]["error_message"] == raw
|
||||
|
||||
|
||||
@patch(STORE_PROMPTS_PATH, return_value=False)
|
||||
def test_other_error_fields_preserved_when_message_redacted(mock_store):
|
||||
"""error_code, error_class, llm_provider, and traceback must survive redaction."""
|
||||
raw = "error with content"
|
||||
result = _get_spend_logs_metadata(_failure_metadata_with_error(raw))
|
||||
info = result["error_information"]
|
||||
assert info["error_code"] == "400"
|
||||
assert info["error_class"] == "BadRequestError"
|
||||
assert info["llm_provider"] == "vertex_ai"
|
||||
assert info["traceback"] == "Traceback ..."
|
||||
assert info["error_message"] is None
|
||||
|
||||
|
||||
@patch(STORE_PROMPTS_PATH, return_value=False)
|
||||
def test_no_error_when_error_information_is_none(mock_store):
|
||||
"""No crash when error_information is absent (success path)."""
|
||||
metadata = {"user_api_key": "sk-test", "status": "success"}
|
||||
result = _get_spend_logs_metadata(metadata)
|
||||
assert result.get("error_information") is None
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue