mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-27 06:13:07 +00:00
fix(proxy): keep startup records as printed instead of running secret redaction
In JSON mode the startup messages ran secret redaction, which the prints they replace never had. Its sk- key pattern has no left boundary, so a model named risk-assessment-gpt4o was logged as riREDACTED in the model list (10-21 gauntlet finding). The records now skip redaction like the prints did; the processed record still counts as scrubbed, so the handler's secret filter and the formatter leave it alone. The stdout cap stays off and base64 collapse stays on, as before. Tests: tests/unit/test_logging.py checks the model name comes out whole (red on7d17f126e5), replacing the test that asserted startup records were redacted; tests/integration/configuration/test_json_logs_startup.py adds a boot with a risk-assessment-<id> model whose model list record carries the name (red on7d17f126e5).
This commit is contained in:
parent
7d17f126e5
commit
ea2ac4a136
3 changed files with 27 additions and 19 deletions
|
|
@ -932,14 +932,10 @@ def print_startup_message(message: str) -> None:
|
|||
# proxy logger (at the root's WARNING in the CLI process until initialize() runs) and of its JSON
|
||||
# handler (LITELLM_LOG). It also skips the logger's stdout cap: that cap bounds echoed request
|
||||
# payloads, while a startup message is operator text the print showed whole (the required migration
|
||||
# SQL, the policy list), so it gets the uncapped processing of a DEBUG line, secret redaction and
|
||||
# base64 collapse included, before the handler's filters and the formatter.
|
||||
if not _process_record(
|
||||
record,
|
||||
base64_limit=_get_max_base64_length_stdout_log(),
|
||||
text_limit=0,
|
||||
redact=_ENABLE_SECRET_REDACTION,
|
||||
):
|
||||
# SQL, the policy list). It skips secret redaction too, which the print never had and whose sk-
|
||||
# pattern turns a model named risk-assessment-gpt4o into riREDACTED. The processed record counts as
|
||||
# scrubbed, so the handler's secret filter and the formatter leave it alone too.
|
||||
if not _process_record(record, base64_limit=_get_max_base64_length_stdout_log(), text_limit=0, redact=False):
|
||||
return
|
||||
# The print wrote to stdout whatever the logging config said, so the record goes through the proxy's
|
||||
# own JSON handler on stdout rather than the proxy logger's handlers: uvicorn applies an operator
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ startup information those lines used to carry is still present as JSON records.
|
|||
on through the environment and through the config, at one and two uvicorn workers, at the INFO, the
|
||||
default and the WARNING log level, under a concurrent burst that lands on both workers, under an operator
|
||||
--log_config that names the proxy logger or gives it a handler of its own, and with a policy list longer
|
||||
than the stdout cap. The deprecated --enforce_prisma_migration_check notice is one JSON record under a
|
||||
config-only json_logs, and a boot whose config cannot be loaded still prints it before failing.
|
||||
than the stdout cap. The model list keeps a model name the secret patterns would match. The deprecated
|
||||
--enforce_prisma_migration_check notice is one JSON record under a config-only json_logs, and a boot whose
|
||||
config cannot be loaded still prints it before failing.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -290,6 +291,20 @@ def test_json_logs_keeps_a_startup_listing_longer_than_the_stdout_cap_whole(gate
|
|||
assert policy in listings[0], (policy, listings[0])
|
||||
|
||||
|
||||
def test_json_logs_model_list_keeps_a_model_name_the_secret_patterns_would_match(
|
||||
gateway: Gateway, tmp_path: Path
|
||||
) -> None:
|
||||
"""The model list printed every model name as configured. The sk- key pattern matches inside a name such as
|
||||
`risk-assessment-...`, so the JSON record must carry that name as the print did, not `riREDACTED`."""
|
||||
models: Final = (f"risk-assessment-{uuid.uuid4().hex}",)
|
||||
config: Final = _write_config(tmp_path, gateway.upstream_url, models, {**CACHE_SETTINGS, "json_logs": True})
|
||||
|
||||
log: Final = _boot_serve_and_stop(gateway, tmp_path, config, {"LITELLM_LOG": "INFO"}, 1, models)
|
||||
|
||||
_assert_every_line_is_json(log, 1, models)
|
||||
assert f" {models[0]}" in _messages(log), _messages(log)
|
||||
|
||||
|
||||
def test_json_logs_default_level_keeps_every_line_json(gateway: Gateway, tmp_path: Path) -> None:
|
||||
"""Without LITELLM_LOG the proxy loggers sit at WARNING; the startup notices a plain-mode operator
|
||||
always sees still arrive, as JSON, and nothing arrives as plaintext."""
|
||||
|
|
|
|||
|
|
@ -2003,24 +2003,21 @@ def test_print_startup_message_keeps_a_listing_longer_than_the_stdout_cap_whole(
|
|||
assert record["message"] == listing
|
||||
|
||||
|
||||
def test_print_startup_message_still_redacts_a_secret_in_the_uncapped_record(monkeypatch, capfd):
|
||||
"""Skipping the stdout cap must not skip secret redaction: a startup message that echoes a credential,
|
||||
such as a failed migration diff quoting the database error, goes out scrubbed."""
|
||||
def test_print_startup_message_keeps_a_model_name_the_secret_patterns_would_match(monkeypatch, capfd):
|
||||
"""The model list printed `risk-assessment-gpt4o` as configured; the sk- key pattern matches inside
|
||||
that name, so the JSON record has to carry the name as the print did, not `riREDACTED`."""
|
||||
monkeypatch.setattr(litellm, "json_logs", True)
|
||||
monkeypatch.setattr(sys, "excepthook", sys.excepthook) # turn_on_json swaps it; put it back at teardown
|
||||
turn_on_json()
|
||||
assert secret_redaction.redact_string("risk-assessment-gpt4o") != "risk-assessment-gpt4o"
|
||||
|
||||
print_startup_message(
|
||||
'Failed to generate migration diff. Error: {"api_key": "sk-abcdefghijklmnopqrstuvwxyz0123456789"}'
|
||||
)
|
||||
print_startup_message("\033[32m risk-assessment-gpt4o\033[0m")
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
assert err == ""
|
||||
lines: Final = [raw for raw in out.splitlines() if raw.strip()]
|
||||
assert len(lines) == 1, lines
|
||||
record: Final = json.loads(lines[0])
|
||||
assert "sk-abcdefghijklmnopqrstuvwxyz0123456789" not in lines[0]
|
||||
assert record["message"] == 'Failed to generate migration diff. Error: {"REDACTED"}'
|
||||
assert json.loads(lines[0])["message"] == " risk-assessment-gpt4o"
|
||||
|
||||
|
||||
def test_print_startup_message_blank_spacer_emits_no_json_record(monkeypatch, capfd):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue