fix; small leaks possible and some greptile feedback

This commit is contained in:
Ryan Crabbe 2026-03-10 16:43:39 -07:00
parent 874ff028d2
commit 3a6632d533
2 changed files with 90 additions and 70 deletions

View file

@ -5,10 +5,7 @@ import re
import sys
from datetime import datetime
from logging import Formatter
from collections.abc import Mapping
from typing import Any, Dict, List, Optional, Tuple, Union
LogRecordArgs = Union[Tuple[Any, ...], Mapping[str, Any]]
from typing import Any, Dict, List, Optional, Union
from litellm.litellm_core_utils.safe_json_dumps import safe_dumps
from litellm.litellm_core_utils.safe_json_loads import safe_json_loads
@ -20,11 +17,7 @@ if set_verbose is True:
"`litellm.set_verbose` is deprecated. Please set `os.environ['LITELLM_LOG'] = 'DEBUG'` for debug logs."
)
_DISABLE_SECRET_REDACTION = os.getenv("LITELLM_DISABLE_REDACTION", "").lower() in (
"1",
"true",
"yes",
)
_ENABLE_SECRET_REDACTION = bool(os.getenv("LITELLM_REDACT_SECRETS", False))
_REDACTED = "REDACTED"
@ -72,30 +65,18 @@ class SecretRedactionFilter(logging.Filter):
"""Scrubs known secret/credential patterns from log records."""
def filter(self, record: logging.LogRecord) -> bool:
if _DISABLE_SECRET_REDACTION:
if not _ENABLE_SECRET_REDACTION:
return True
if isinstance(record.msg, str):
record.msg = _redact_string(record.msg)
if record.args:
record.args = self._redact_args(record.args)
try:
record.msg = _redact_string(record.getMessage())
record.args = None
except Exception:
if isinstance(record.msg, str):
record.msg = _redact_string(record.msg)
return True
@staticmethod
def _redact_args(args: LogRecordArgs) -> LogRecordArgs:
if isinstance(args, Mapping):
return {
k: _redact_string(v) if isinstance(v, str) else v
for k, v in args.items()
}
if isinstance(args, tuple):
return tuple(
_redact_string(a) if isinstance(a, str) else a for a in args
)
return args
_secret_filter = SecretRedactionFilter()
@ -401,7 +382,6 @@ def _turn_on_json():
"""
handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
handler.addFilter(_secret_filter)
_initialize_loggers_with_handler(handler)
# Set up exception handlers
_setup_json_exception_handlers(JsonFormatter())

View file

@ -1,14 +1,10 @@
import logging
import os
import sys
from io import StringIO
from unittest.mock import patch
import pytest
sys.path.insert(0, os.path.abspath("../../.."))
from litellm._logging import (
SecretRedactionFilter,
_redact_string,
_secret_filter,
verbose_logger,
@ -16,32 +12,18 @@ from litellm._logging import (
verbose_router_logger,
)
def test_redact_string_covers_all_secret_formats():
secrets = {
"AWS access key": "AKIAIOSFODNN7EXAMPLE",
"AWS temp key": "ASIAISAMPLEKEYID1234",
"AWS secret": "aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"AWS session token": "aws_session_token: FwoGZXIvYXdzEBYaDHqa0AP1RIF0re2EXAMPLETOKEN1234567890",
"Bearer token": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.payload.sig",
"OpenAI key": "sk-proj-abc123def456ghi789jklmnopqrst",
"x-api-key header": "x-api-key: mysecretapikey123",
"api-key header": "api-key: mysecretapikey123",
"Azure key": "api_key=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"Google key": "AIzaSyA1234567890abcdefghijklmnopqrstuvwx",
"Anthropic x-ak": "x-ak-abcdefghijklmnopqrstuvwxyz12345",
"Dict repr with secrets": "Request Headers: {'Authorization': 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.payload.sig', 'x-api-key': 'sk-ant-api03-abcdefghijklmnopqrstuvwxyz'}",
}
for label, secret in secrets.items():
result = _redact_string(f"msg: {secret}")
assert secret not in result, f"{label} was not redacted"
assert "REDACTED" in result, f"{label} missing REDACTED marker"
normal = "Loaded model gpt-4 with 3 replicas on us-east-1"
assert _redact_string(normal) == normal
SECRET = "sk-proj-abc123def456ghi789jklmnopqrst"
def test_filter_redacts_secrets_in_logger_output():
@pytest.fixture(autouse=True)
def _enable_redaction():
"""Enable secret redaction for all tests in this module."""
with patch("litellm._logging._ENABLE_SECRET_REDACTION", True):
yield
def _capture_logger_output(fn):
"""Run fn with all litellm loggers wired to a StringIO buffer, return output."""
buf = StringIO()
h = logging.StreamHandler(buf)
h.addFilter(_secret_filter)
@ -51,22 +33,80 @@ def test_filter_redacts_secrets_in_logger_output():
lg.handlers.clear()
lg.addHandler(h)
lg.setLevel(logging.DEBUG)
try:
verbose_logger.debug(f"Credentials: AKIAIOSFODNN7EXAMPLE")
verbose_proxy_logger.debug(f"Headers: Bearer eyJhbGciOiJSUzI1NiJ9.payload.sig")
verbose_router_logger.debug(f"Key: sk-proj-abc123def456ghi789jklmnopqrst")
verbose_logger.debug("Normal message with no secrets")
output = buf.getvalue()
assert "AKIAIOSFODNN7EXAMPLE" not in output
assert "eyJhbGciOiJSUzI1NiJ9" not in output
assert "sk-proj-abc123def456ghi789jklmnopqrst" not in output
assert "REDACTED" in output
assert "Normal message with no secrets" in output
fn()
return buf.getvalue()
finally:
for lg, handlers, level in saved:
lg.handlers.clear()
for old_h in handlers:
lg.addHandler(old_h)
lg.setLevel(level)
def test_redact_string_catches_secret_patterns():
"""Core regex patterns redact known secret formats."""
cases = [
"Bearer eyJhbGciOiJSUzI1NiJ9.payload.sig",
"api_key=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"password=supersecretpassword123",
"postgresql://admin:s3cretpass@db.example.com:5432/mydb",
SECRET,
]
for secret in cases:
result = _redact_string("msg: " + secret)
assert secret not in result, f"{secret!r} was not redacted"
assert "REDACTED" in result
normal = "Loaded model gpt-4 with 3 replicas on us-east-1"
assert _redact_string(normal) == normal
def test_filter_redacts_secrets_in_logger_output():
def log_messages():
verbose_logger.debug("Key: " + SECRET)
verbose_logger.debug("Normal message with no secrets")
output = _capture_logger_output(log_messages)
assert SECRET not in output
assert "REDACTED" in output
assert "Normal message with no secrets" in output
def test_filter_redacts_percent_style_args():
"""Secrets passed as %-style args should be redacted."""
def log_messages():
verbose_logger.debug("key=%s region=%s", SECRET, "us-east-1")
output = _capture_logger_output(log_messages)
assert SECRET not in output
assert "us-east-1" in output
def test_filter_redacts_non_string_args():
"""Secrets inside dicts/lists passed as %-style args should be redacted."""
def log_messages():
verbose_logger.debug("Config: %s", {"nested": {"key": SECRET}})
verbose_logger.debug("Keys: %s", [SECRET])
output = _capture_logger_output(log_messages)
assert SECRET not in output
assert "REDACTED" in output
def test_disable_redaction_passes_secrets_through(_enable_redaction):
"""When _ENABLE_SECRET_REDACTION is False, secrets pass through."""
with patch("litellm._logging._ENABLE_SECRET_REDACTION", False):
record = logging.LogRecord(
name="test",
level=logging.DEBUG,
pathname="",
lineno=0,
msg="key=" + SECRET,
args=(),
exc_info=None,
)
_secret_filter.filter(record)
assert "sk-proj-" in record.msg