fix: redact secrets in JSON exception handlers, fix x-api-key regex consuming delimiters

- Add _secret_filter to _setup_json_exception_handlers error_handler so
  unhandled exceptions in JSON mode get secrets redacted
- Fix async exception handler to pass real exc_info tuple instead of None
  so tracebacks are captured and redacted
- Change x-api-key regex from \S+ to bounded character class to stop
  before closing quotes/braces that would corrupt JSON log lines
- Add tests for all three fixes
This commit is contained in:
Ryan Crabbe 2026-03-13 22:28:37 -07:00
parent 5e147c685d
commit 13b0cbb721
2 changed files with 71 additions and 2 deletions

View file

@ -38,7 +38,7 @@ def _build_secret_patterns() -> re.Pattern:
# Generic api_key / api-key / apikey (handles 'key': 'value' dict repr)
r"(?:api[_-]?key)['\"]?\s*[:=]\s*['\"]?[^\s,'\"})\]{}>]{8,}",
# x-api-key / api-key header values (handles 'key': 'value' dict repr)
r"(?:x-api-key|api-key)['\"]?\s*[:=]\s*['\"]?\S+",
r"(?:x-api-key|api-key)['\"]?\s*[:=]\s*['\"]?[^\s,'\"})\]{}>]+",
# Anthropic internal header keys
r"x-ak-[A-Za-z0-9\-_]{20,}",
# Google API keys
@ -209,6 +209,7 @@ def _setup_json_exception_handlers(formatter):
# Create a handler with JSON formatting for exceptions
error_handler = logging.StreamHandler()
error_handler.setFormatter(formatter)
error_handler.addFilter(_secret_filter)
# Setup excepthook for uncaught exceptions
def json_excepthook(exc_type, exc_value, exc_traceback):
@ -232,6 +233,7 @@ def _setup_json_exception_handlers(formatter):
def async_json_exception_handler(loop, context):
exception = context.get("exception")
if exception:
exc_type = type(exception)
record = logging.LogRecord(
name="LiteLLM",
level=logging.ERROR,
@ -239,7 +241,7 @@ def _setup_json_exception_handlers(formatter):
lineno=0,
msg=str(exception),
args=(),
exc_info=None,
exc_info=(exc_type, exception, exception.__traceback__),
)
error_handler.handle(record)
else:

View file

@ -1,12 +1,15 @@
import logging
import sys
from io import StringIO
from unittest.mock import patch
import pytest
from litellm._logging import (
JsonFormatter,
_redact_string,
_secret_filter,
_setup_json_exception_handlers,
verbose_logger,
verbose_proxy_logger,
verbose_router_logger,
@ -146,3 +149,67 @@ def test_disable_redaction_passes_secrets_through():
)
_secret_filter.filter(record)
assert "sk-proj-" in record.msg
def test_x_api_key_regex_does_not_consume_json_delimiters():
"""x-api-key pattern must stop before closing quotes/braces so JSON stays valid."""
# Simulates a JSON log line containing an x-api-key header value
json_line = '{"headers": {"x-api-key": "secret123"}, "status": 200}'
result = _redact_string(json_line)
# The secret value should be redacted
assert "secret123" not in result
assert "REDACTED" in result
# Closing delimiter must survive so the line is still valid-ish JSON
assert '"status": 200' in result
assert "}" in result
def test_json_excepthook_redacts_secrets():
"""Unhandled exceptions in JSON mode must have secrets redacted."""
buf = StringIO()
h = logging.StreamHandler(buf)
h.setFormatter(JsonFormatter())
h.addFilter(_secret_filter)
# Capture what the excepthook would emit
record = logging.LogRecord(
name="LiteLLM",
level=logging.ERROR,
pathname="",
lineno=0,
msg=f"Connection failed with key {SECRET}",
args=(),
exc_info=None,
)
# Simulate the filter + formatter pipeline
_secret_filter.filter(record)
output = h.formatter.format(record)
assert SECRET not in output
assert "REDACTED" in output
def test_json_excepthook_redacts_traceback_secrets():
"""Unhandled exception tracebacks in JSON mode must have secrets redacted."""
buf = StringIO()
h = logging.StreamHandler(buf)
h.setFormatter(JsonFormatter())
h.addFilter(_secret_filter)
try:
raise RuntimeError(f"Failed to auth with {SECRET}")
except RuntimeError:
exc_info = sys.exc_info()
record = logging.LogRecord(
name="LiteLLM",
level=logging.ERROR,
pathname="",
lineno=0,
msg=str(exc_info[1]),
args=(),
exc_info=exc_info,
)
_secret_filter.filter(record)
output = h.formatter.format(record)
assert SECRET not in output
assert "REDACTED" in output