mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-06 02:48:13 +00:00
fix: redact secrets in exception tracebacks and extra fields, normalize env var check
- Redact exc_info tracebacks so secrets in exception messages don't leak
- Redact string values in extra={} fields passed to loggers
- Use case-insensitive check for LITELLM_REDACT_SECRETS env var (consistent with rest of codebase)
- Remove unused Union import
This commit is contained in:
parent
9173c1abce
commit
bd16532431
2 changed files with 54 additions and 2 deletions
|
|
@ -5,7 +5,7 @@ import re
|
|||
import sys
|
||||
from datetime import datetime
|
||||
from logging import Formatter
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from litellm.litellm_core_utils.safe_json_dumps import safe_dumps
|
||||
from litellm.litellm_core_utils.safe_json_loads import safe_json_loads
|
||||
|
|
@ -17,7 +17,7 @@ if set_verbose is True:
|
|||
"`litellm.set_verbose` is deprecated. Please set `os.environ['LITELLM_LOG'] = 'DEBUG'` for debug logs."
|
||||
)
|
||||
|
||||
_ENABLE_SECRET_REDACTION = os.getenv("LITELLM_REDACT_SECRETS") == "TRUE"
|
||||
_ENABLE_SECRET_REDACTION = os.getenv("LITELLM_REDACT_SECRETS", "").lower() == "true"
|
||||
|
||||
_REDACTED = "REDACTED"
|
||||
|
||||
|
|
@ -64,6 +64,8 @@ def _redact_string(value: str) -> str:
|
|||
class SecretRedactionFilter(logging.Filter):
|
||||
"""Scrubs known secret/credential patterns from log records."""
|
||||
|
||||
_formatter = logging.Formatter()
|
||||
|
||||
def filter(self, record: logging.LogRecord) -> bool:
|
||||
if not _ENABLE_SECRET_REDACTION:
|
||||
return True
|
||||
|
|
@ -75,6 +77,20 @@ class SecretRedactionFilter(logging.Filter):
|
|||
if isinstance(record.msg, str):
|
||||
record.msg = _redact_string(record.msg)
|
||||
|
||||
# Redact exception tracebacks
|
||||
if record.exc_info and record.exc_info[1] is not None:
|
||||
try:
|
||||
record.exc_text = _redact_string(
|
||||
self._formatter.formatException(record.exc_info)
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Redact extra fields passed via logger.debug("msg", extra={...})
|
||||
for key, value in record.__dict__.items():
|
||||
if key not in _STANDARD_RECORD_ATTRS and isinstance(value, str):
|
||||
setattr(record, key, _redact_string(value))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,42 @@ def test_filter_redacts_non_string_args():
|
|||
assert "REDACTED" in output
|
||||
|
||||
|
||||
def test_filter_redacts_exception_tracebacks():
|
||||
"""Secrets embedded in exception messages must be redacted in tracebacks."""
|
||||
|
||||
def log_messages():
|
||||
try:
|
||||
raise ValueError(f"Auth failed with key {SECRET}")
|
||||
except ValueError:
|
||||
verbose_logger.exception("Something went wrong")
|
||||
|
||||
output = _capture_logger_output(log_messages)
|
||||
assert SECRET not in output
|
||||
assert "REDACTED" in output
|
||||
assert "Something went wrong" in output
|
||||
|
||||
|
||||
def test_filter_redacts_extra_fields():
|
||||
"""Secrets passed via extra={...} must be redacted on the record."""
|
||||
record = logging.LogRecord(
|
||||
name="test",
|
||||
level=logging.DEBUG,
|
||||
pathname="",
|
||||
lineno=0,
|
||||
msg="request completed",
|
||||
args=(),
|
||||
exc_info=None,
|
||||
)
|
||||
record.api_key = SECRET
|
||||
record.region = "us-east-1"
|
||||
|
||||
_secret_filter.filter(record)
|
||||
|
||||
assert SECRET not in record.api_key
|
||||
assert "REDACTED" in record.api_key
|
||||
assert record.region == "us-east-1"
|
||||
|
||||
|
||||
def test_disable_redaction_passes_secrets_through():
|
||||
"""When _ENABLE_SECRET_REDACTION is False, secrets pass through."""
|
||||
with patch("litellm._logging._ENABLE_SECRET_REDACTION", False):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue