Skip to content

fix(proxy): restore admin key/team callback_vars.turn_off_message_logging override (LIT-3587) - #31905

Merged
yucheng-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_key_msg_redaction
Jul 2, 2026
Merged

fix(proxy): restore admin key/team callback_vars.turn_off_message_logging override (LIT-3587)#31905
yucheng-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_key_msg_redaction

Conversation

@yucheng-berri

@yucheng-berri yucheng-berri commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Linear ticket

Resolves LIT-3587

Pre-Submission checklist

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests) locally
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of 5/5

Screenshots / Proof of Fix

Setup used for every run below. Local proxy on :4001, real Postgres, no mocks in the proxy path. Config in the "off" runs flips turn_off_message_logging from true to false.

model_list:
  - model_name: mock-model
    litellm_params:
      model: openai/gpt-3.5-turbo
      api_key: fake
      mock_response: "hello from mock"
litellm_settings:
  turn_off_message_logging: true       # global redaction ON (or false for the mirror run)
general_settings:
  master_key: sk-1234
  store_prompts_in_spend_logs: true    # so the actual logged payload is inspectable
  database_url: "postgresql://..."

Every observation below reads LiteLLM_SpendLogs.proxy_server_request.messages[0].content. That is the actual logged payload a callback (Datadog, Langfuse, etc.) or the spend-logs consumer would see. If that column shows redacted-by-litellm, the message was hidden; if it shows the sent string, the message was preserved.

Run 1 — Global ON, the customer's scenario

Two keys:

curl -X POST 'http://localhost:4001/key/generate' -H 'Authorization: Bearer sk-1234' -H 'Content-Type: application/json' \
  -d '{"metadata":{"logging":[{"callback_name":"langfuse","callback_type":"success_and_failure","callback_vars":{"turn_off_message_logging":false}}]}}'

curl -X POST 'http://localhost:4001/key/generate' -H 'Authorization: Bearer sk-1234' -H 'Content-Type: application/json' -d '{}'

Identical chat completion from each key. Result:

            content
-------------------------------
 redacted-by-litellm            (control key, no override)
 RUN1-admin-disables-redaction  (admin-override key)

Admin override wins; global keeps redacting for the control key. This is the exact behavior the ticket asked to restore.

Run 2 — Client bypass attempts, four shapes

# top-level body
{"model":"mock-model","turn_off_message_logging":false,"messages":[...]}

# nested in metadata
{"model":"mock-model","metadata":{"turn_off_message_logging":false},"messages":[...]}

# JSON-string litellm_metadata (multipart/form-data / extra_body shape)
{"model":"mock-model","litellm_metadata":"{\"turn_off_message_logging\":false}","messages":[...]}

# NEW: nested in litellm_params.metadata (previously a live bypass, now blocked)
{"model":"mock-model","litellm_params":{"metadata":{"turn_off_message_logging":false}},"messages":[...]}

Each returned:

top-level                  ->  HTTP 401
metadata                   ->  HTTP 401
litellm_metadata           ->  HTTP 401
litellm_params.metadata    ->  HTTP 401

The 401 message is the same one the earlier huntr fix produces: turn_off_message_logging is not allowed in request body. Clientside passthrough requires explicit admin opt-in via either general_settings.allow_client_side_credentials = true or configurable_clientside_auth_params on the deployment. All four attack surfaces converge on the auth-layer banned-list, so a fifth attack shape added later would need to bypass one of them.

_strip_client_message_redaction_opt_out in add_litellm_data_to_request is defense-in-depth for the same four surfaces; it fires when a deployment opens the auth-layer 401 via configurable_clientside_auth_params: [turn_off_message_logging].

Run 3 — Global OFF, admin selectively enables redaction (the mirror direction)

The ticket flagged both directions as broken. Repeated Run 1 with turn_off_message_logging: false at the global level:

# admin key with callback_vars.turn_off_message_logging = true
# control key with no override

Result:

         content
--------------------------
 RUN3-control-no-override  (control key, global default preserved)
 redacted-by-litellm       (admin-override key, redaction enabled per key)

Admin can now flip either direction per key. Global is the fallback; the key/team override wins.

Investigator's live probe that surfaced the litellm_params.metadata bypass

During code review, initialize_standard_callback_dynamic_params was observed to merge kwargs["litellm_params"]["metadata"] into its extraction path, but the first version of the proxy strip only covered metadata and litellm_metadata. A single-request probe confirmed the bypass on the previous commit:

                  request_id                   |             content             | nested_flag
-----------------------------------------------+---------------------------------+-------------
 chatcmpl-61871bd7-3b44-489e-b7ac-3ebabe9e4474 | PROBE-A-litellm_params-metadata | false

Content leaked through under nested_flag: false. The current commit closes it two ways: the auth-layer now descends into litellm_params.metadata alongside metadata and litellm_metadata, and the shared iter_client_callback_metadata_dicts helper makes the extractor and the strip walk the same set of slots so they cannot drift again.

Type

Bug Fix

Changes

The security fix in 34e9be1ba7 removed turn_off_message_logging from _supported_callback_params to stop callers bypassing global redaction via the request body. That also killed the documented admin-only per-key or per-team override, because both flows resolve through the same allowlist in initialize_standard_callback_dynamic_params.

This PR restores turn_off_message_logging to _supported_callback_params so an admin-configured metadata.logging[].callback_vars.turn_off_message_logging survives into StandardCallbackDynamicParams and can override the global setting for that key or team, matching the documented behavior at docs/proxy/team_logging#disableenable-message-redaction.

Client bypass now sits on three layers. First, restoring the field re-enrolls it in the auth layer's _BANNED_REQUEST_BODY_PARAMS (derived from _supported_callback_params via _build_banned_observability_params). is_request_body_safe descends into metadata, litellm_metadata, extra_body, litellm_embedding_config, and now litellm_params.metadata, so every one of the four attack shapes returns 401 at ingress. Second, _strip_client_message_redaction_opt_out in add_litellm_data_to_request walks the same slot list via a shared helper and removes any leftover client-supplied opt-out when global redaction is on and the key or team lacks allow_client_message_redaction_opt_out. Third, the admin callback_vars unpack runs after the strip, so admin-written values are the last write and always take precedence.

Consolidated the extractor and the proxy strip on one shared iterator, iter_client_callback_metadata_dicts in litellm_core_utils/initialize_dynamic_callback_params.py. Both callers walk metadata, litellm_metadata, and litellm_params.metadata from the same source of truth. A mutation-checked unit test (test_extractor_reads_turn_off_message_logging_from_every_slot) fails immediately if a future edit shrinks the slot set.

Regression tests cover: the admin key and team paths in both directions (global on + admin false, global off + admin true), all four client-body strip surfaces including litellm_params.metadata, the allow_client_message_redaction_opt_out opt-in path, and the auth-layer banned-param descent into litellm_params.metadata. Two dynamic-param e2e tests in tests/logging_callback_tests/ were flipped to reflect that the dynamic param now correctly overrides the global setting.

Co-authored-by: Cursor Agent (auth-layer is_request_body_safe descent into litellm_params.metadata).


Note

Medium Risk
Touches message redaction and request-body observability controls; changes restore admin overrides but rely on auth bans and stripping to keep clients from disabling global redaction without explicit opt-in.

Overview
Restores per-key/team message redaction overrides that broke when turn_off_message_logging was dropped from the dynamic callback allowlist. The field is back on _supported_callback_params, so admin metadata.logging[].callback_vars.turn_off_message_logging again reaches StandardCallbackDynamicParams and can override the global litellm.turn_off_message_logging setting in either direction.

Client bypass is tightened in parallel: the auth bouncer now descends into litellm_params.metadata (in addition to metadata / litellm_metadata), and a new _strip_client_message_redaction_opt_out removes client-supplied “disable redaction” values from the top level and all metadata slots when global redaction is on and the key/team has not opted in via allow_client_message_redaction_opt_out. Extraction and stripping share iter_client_callback_metadata_dicts so the same three slots are always walked; metadata lookup no longer merges metadata with litellm_params.metadata in a way that inverted precedence—litellm_params.metadata wins over metadata when both set the same callback param.

E2E logging tests now expect request-level turn_off_message_logging to drive redaction again when it reaches the SDK path; proxy tests cover admin callback vars, multi-slot stripping, and auth rejection of nested litellm_params.metadata.

Reviewed by Cursor Bugbot for commit 8d9fbef. Bugbot is set up for automated code reviews on this repo. Configure here.

@yucheng-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

@CLAassistant

CLAassistant commented Jul 1, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


yucheng seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@yucheng-berri
yucheng-berri force-pushed the litellm_key_msg_redaction branch from ae2c43c to 61abefe Compare July 1, 2026 20:33
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR restores turn_off_message_logging as a dynamic callback param so admin-configured callback_vars on keys and teams can again override the global message-redaction setting in both directions, while keeping client-supplied values blocked at the proxy through three layered defenses.

  • Auth-layer hardening: is_request_body_safe now descends into litellm_params.metadata alongside metadata and litellm_metadata, closing a previously unguarded attack surface for banned observability fields.
  • Shared metadata iterator: iter_client_callback_metadata_dicts provides a single, consistent three-slot walk (litellm_params.metadatalitellm_metadatametadata) used by both the callback-param extractor and the new _strip_client_message_redaction_opt_out defense-in-depth helper, preventing the two from drifting out of sync.
  • Correct ordering: the strip runs after litellm_metadata string-to-dict parsing and before admin callback_vars application, so admin-written values are always the last write and cannot be removed by the strip.

Confidence Score: 5/5

Safe to merge — the three-layer defense (auth reject → strip → admin last-write) is correctly ordered and fully tested for all four identified attack surfaces.

All security-relevant ordering constraints are upheld: the strip fires after litellm_metadata JSON parsing and before admin callback_vars are applied, so admin values always win. The shared iter_client_callback_metadata_dicts iterator eliminates the extractor/strip drift risk. New auth descent into litellm_params.metadata closes the bypass path documented in the PR. Test coverage spans the auth layer, the strip layer, and the admin-override path in both redaction directions for both key and team metadata.

No files require special attention.

Important Files Changed

Filename Overview
litellm/litellm_core_utils/initialize_dynamic_callback_params.py Adds iter_client_callback_metadata_dicts shared iterator for consistent three-slot metadata walking (litellm_params.metadata > litellm_metadata > metadata); restores turn_off_message_logging to _supported_callback_params; refactors step-2 extraction to use the shared iterator. Logic is correct and precedence matches the old merge order.
litellm/proxy/auth/auth_utils.py Extends is_request_body_safe to descend into litellm_params.metadata and run _check_banned_params there, closing the previously unreported litellm_params.metadata attack surface; change is additive and does not alter existing nested-key or metadata-key checks.
litellm/proxy/litellm_pre_call_utils.py Introduces _strip_client_message_redaction_opt_out (defense-in-depth strip using shared iterator) and moves its invocation to after litellm_metadata string-to-dict parse; removes the single-slot inline strip; admin callback_vars still apply after the strip at lines 1677–1680, preserving last-write semantics.
tests/test_litellm/proxy/test_litellm_pre_call_utils.py Extends strip test to cover litellm_params.metadata slot; adds two new parametrized admin-override tests (key and team, both directions) and verifies should_redact_message_logging end-to-end.
tests/logging_callback_tests/test_logging_redaction_e2e_test.py Renames and re-parameterizes two e2e SDK tests to reflect that dynamic turn_off_message_logging now overrides the global setting; behavior change is intentional and proxy-path security is covered by separate proxy-layer tests.
tests/test_litellm/litellm_core_utils/test_initialize_dynamic_callback_params.py Adds mutation-checked extractor tests for all three metadata slots; converts test_turn_off_message_logging_not_extracted_from_request to verify extraction now succeeds, matching the restored _supported_callback_params membership.
tests/test_litellm/proxy/auth/test_auth_utils.py Adds test_observability_field_in_litellm_params_metadata_is_rejected to confirm the new litellm_params.metadata descent in is_request_body_safe raises correctly on banned observability fields.

Reviews (7): Last reviewed commit: "fix(proxy): restore admin key/team callb..." | Re-trigger Greptile

@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR restores per-key and per-team callback_vars.turn_off_message_logging overrides by re-adding turn_off_message_logging to _supported_callback_params, while keeping client-side bypass attempts blocked through the auth layer and a new defense-in-depth strip helper.

  • initialize_dynamic_callback_params.py: one-line restoration of turn_off_message_logging to the allowlist; because _BANNED_REQUEST_BODY_PARAMS is derived from this allowlist via _build_banned_observability_params, client requests carrying the field are still rejected at the auth layer.
  • litellm_pre_call_utils.py: the old narrow strip (top-level only, ran before the litellm_metadata string-to-dict parse) is replaced by _strip_client_message_redaction_opt_out, which covers top-level, metadata, and parsed litellm_metadata; it runs after the parse and before admin callback_vars are unpacked, so the ordering guarantees admin values always win.
  • Tests: new assertions cover the admin key and team paths end-to-end (value reaches StandardCallbackDynamicParams and flips should_redact_message_logging), and the existing bypass-strip test is extended to verify metadata and litellm_metadata surfaces.

Confidence Score: 4/5

Safe to merge; the admin override path is correctly restored and the defense-in-depth strip runs in the right position relative to both the string-to-dict parse and the admin callback_vars unpack.

The two-layer protection is correctly ordered and covers all three client-supplied surfaces. Admin callback_vars are injected after the strip, so they always take precedence. Tests cover the admin key, admin team, client bypass, and allow_client_message_redaction_opt_out paths. The only observations are style-level.

No files require special attention; litellm/proxy/litellm_pre_call_utils.py has the most logic but the ordering of strip, snapshot, and admin unpack is correct.

Important Files Changed

Filename Overview
litellm/litellm_core_utils/initialize_dynamic_callback_params.py Restores turn_off_message_logging to _supported_callback_params, enabling admin key/team callback_vars overrides to flow through initialize_standard_callback_dynamic_params into StandardCallbackDynamicParams.
litellm/proxy/litellm_pre_call_utils.py Adds _strip_client_message_redaction_opt_out as defense-in-depth, covering top-level, metadata, and JSON-string-parsed litellm_metadata; replaces an earlier, narrower strip that ran before the string-to-dict parse. The strip runs before admin callback_vars are unpacked, preserving the correct precedence.
tests/test_litellm/litellm_core_utils/test_initialize_dynamic_callback_params.py Replaces the old test that blocked extraction with parametrized tests that verify extraction now works for bool False, string "False", and metadata-nested True; reflects the new layered security model.
tests/test_litellm/proxy/test_litellm_pre_call_utils.py Adds new test for admin key and team callback_vars paths; extends existing bypass-strip test to cover metadata and litellm_metadata surfaces; adds the allow_client_message_redaction_opt_out path.

Comments Outside Diff (2)

  1. litellm/proxy/litellm_pre_call_utils.py, line 305-306 (link)

    Missing docstring on _strip_client_message_redaction_opt_out. The parallel function _strip_client_pricing_overrides directly above it has a docstring that explains the semantics, the opt-in key, and the timing constraint (must run after string-to-dict parse). The same context is equally important here since operators encountering the debug log line will want to know when the function runs and under what conditions it is skipped.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

  2. litellm/proxy/litellm_pre_call_utils.py, line 1483-1484 (link)

    Strip condition scope worth documenting

    _strip_client_message_redaction_opt_out runs only when litellm.turn_off_message_logging is True. This is correct — admin callback_vars at lines 1677–1680 overwrite any client-supplied value in data regardless, so per-key overrides are protected by ordering. A brief inline comment explaining why this is a strict is True check (rather than truthy) would prevent a future reader from widening it in a way that silently breaks deployments where the setting defaults to None.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Reviews (1): Last reviewed commit: "fix(proxy): restore admin key/team callb..." | Re-trigger Greptile

Comment thread litellm/proxy/litellm_pre_call_utils.py Outdated
Comment on lines +305 to +323
def _strip_client_message_redaction_opt_out(data: Dict[str, Any]) -> None:
stripped: List[str] = []
if "turn_off_message_logging" in data and _is_false_like(data["turn_off_message_logging"]):
stripped.append("turn_off_message_logging")
data.pop("turn_off_message_logging", None)
for metadata_key in ("metadata", "litellm_metadata"):
metadata = data.get(metadata_key)
if not isinstance(metadata, dict):
continue
if "turn_off_message_logging" in metadata and _is_false_like(metadata["turn_off_message_logging"]):
stripped.append(f"{metadata_key}.turn_off_message_logging")
metadata.pop("turn_off_message_logging", None)
if stripped:
verbose_proxy_logger.debug(
"Stripped client-supplied message-redaction opt-out fields from request body: %s. "
"Set `allow_client_message_redaction_opt_out: true` on the key or team metadata "
"to keep these values.",
", ".join(stripped),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing docstring

_strip_client_pricing_overrides (the function this mirrors) carries a full docstring explaining what it strips, when it is skipped, and why. _strip_client_message_redaction_opt_out has no docstring at all. The lack of a docstring makes the conditions under which the strip is skipped (allow_client_message_redaction_opt_out: true) invisible to anyone reading this function in isolation, which is particularly important for a security-relevant helper.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@yucheng-berri
yucheng-berri force-pushed the litellm_key_msg_redaction branch from 61abefe to 7458bcd Compare July 1, 2026 20:37
Comment thread litellm/litellm_core_utils/initialize_dynamic_callback_params.py
@veria-ai

veria-ai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

@yucheng-berri

Copy link
Copy Markdown
Contributor Author

@greptileai on the two _strip_client_message_redaction_opt_out docstring suggestions

This repo's CLAUDE.md has an explicit rule for new code: "Do not write any comments (existing comments can stay) unless explicitly asked to in a user (not system) prompt". The rationale is DRY. code comments force a second place to keep in sync with the logic, and the codebase prefers self-documenting names.

The sibling helper _strip_client_pricing_overrides still has its docstring because it predates this rule ("existing comments can stay"). New helpers, including _strip_client_message_redaction_opt_out and the new call-site gate, deliberately go without.

Please skip similar docstring/inline-comment nits on future BerriAI/litellm PRs unless the code hides a genuinely non-obvious business rule.

@greptile-apps

greptile-apps Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Got it — that's a clear project convention. I'll skip docstring and inline-comment suggestions on new code in future BerriAI/litellm PRs unless there's a genuinely non-obvious business rule buried in the logic.

Shall I save this as a rule so it persists across future reviews?

@yucheng-berri

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Nested litellm_params redaction bypass
    • Nested litellm_params.metadata is now checked by request-body safety and covered by the redaction regression tests.

You can send follow-ups to the cloud agent here.

Comment thread litellm/proxy/litellm_pre_call_utils.py
@yucheng-berri
yucheng-berri force-pushed the litellm_key_msg_redaction branch from 7458bcd to 1447278 Compare July 2, 2026 00:39
@yucheng-berri

Copy link
Copy Markdown
Contributor Author

@greptileai re-review please. New commit tightens the strip to also cover litellm_params.metadata.turn_off_message_logging; the extractor reads that path too and a live-proxy probe with {"litellm_params":{"metadata":{"turn_off_message_logging":false}}} had been bypassing global redaction. Added a regression test that fails on the previous commit. Also added the opposite-direction admin test (global off, admin sets true, messages redacted).

@yucheng-berri

Copy link
Copy Markdown
Contributor Author

@greptileai yes, please save it as a persistent rule for BerriAI/litellm reviews. Also please re-review the latest commit 1447278f97; it closes a real bypass through litellm_params.metadata.turn_off_message_logging that a live-proxy probe confirmed on the previous commit.

@yucheng-berri
yucheng-berri force-pushed the litellm_key_msg_redaction branch 2 times, most recently from d6894f9 to fee0bb2 Compare July 2, 2026 00:56
@yucheng-berri

Copy link
Copy Markdown
Contributor Author

@greptileai re-review please. New commit fee0bb29aa does three things: (1) consolidates the metadata traversal so initialize_standard_callback_dynamic_params and _strip_client_message_redaction_opt_out walk the same slots via one iter_client_callback_metadata_dicts helper — the extractor/strip drift that let litellm_params.metadata bypass is now impossible without changing the shared helper; (2) squashes in the concurrent auth-layer fix for is_request_body_safe descending into litellm_params.metadata (co-author: Cursor Agent); (3) adds an invariant unit test that asserts the extractor reads turn_off_message_logging from every slot the helper yields.

@yucheng-berri

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Callback param slot precedence inverted
    • Metadata callback params now resolve with later metadata slots overriding earlier ones, restoring litellm_params.metadata precedence over metadata with a regression test.

You can send follow-ups to the cloud agent here.

Comment thread litellm/litellm_core_utils/initialize_dynamic_callback_params.py
@yucheng-berri
yucheng-berri force-pushed the litellm_key_msg_redaction branch from 6e03de8 to 6535cd1 Compare July 2, 2026 01:19
@yucheng-berri

Copy link
Copy Markdown
Contributor Author

@greptileai good catch on the inverted precedence. Latest commit 6535cd1b94 preserves the pre-refactor merge semantics: iter_client_callback_metadata_dicts is iterated in reversed order at the extractor call-site (thanks to the co-authored fix from Cursor Agent) so litellm_params.metadata still overrides metadata. Added a mutation-checked regression test test_litellm_params_metadata_overrides_metadata plus test_top_level_kwargs_overrides_metadata_slots. Full live-proxy re-run on this commit: all 8 scenarios pass (global-on customer scenario, global-off mirror direction, 4 attack shapes all 401'd including litellm_params.metadata).

@greptile-apps

greptile-apps Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

The fix is correct. The reversed(tuple(iter_client_callback_metadata_dicts(kwargs))) + if param not in standard_callback_dynamic_params guard gives the right precedence: litellm_params.metadata is processed first and wins, then litellm_metadata, then metadata — exactly the semantics described.

The mutation-checked regression test is the right safety net here since the shared iterator means the extractor and strip walk the same slots, so a future edit that adds or removes a slot from iter_client_callback_metadata_dicts will surface in both callers' tests simultaneously.

LGTM.

@yucheng-berri

Copy link
Copy Markdown
Contributor Author

@greptileai re-review requested on HEAD 6535cd1b94. This includes the precedence fix you flagged: iter_client_callback_metadata_dicts is consumed in reversed order at the extractor call-site so litellm_params.metadata overrides metadata again, matching the pre-refactor merge semantics. New tests test_litellm_params_metadata_overrides_metadata and test_top_level_kwargs_overrides_metadata_slots lock the precedence in.

@yucheng-berri

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 6535cd1. Configure here.

…ging override (LIT-3587)

The security fix in 34e9be1 removed turn_off_message_logging from
_supported_callback_params to stop callers bypassing global redaction via
the request body. That also killed the documented admin-only per-key or
per-team override because both flows resolve through the same allowlist
in initialize_standard_callback_dynamic_params.

Put turn_off_message_logging back in _supported_callback_params so an
admin-configured metadata.logging[].callback_vars.turn_off_message_logging
survives into StandardCallbackDynamicParams and can override the global
setting for that key or team, as documented at
docs/proxy/team_logging#disableenable-message-redaction.

Consolidate the metadata traversal so the extractor and the proxy strip
walk the same set of client-controllable slots. iter_client_callback_metadata_dicts
in litellm_core_utils/initialize_dynamic_callback_params.py is the single
source of truth for metadata, litellm_metadata, and litellm_params.metadata;
_strip_client_message_redaction_opt_out imports it so a future addition
to one side automatically reaches the other. The extractor iterates the
helper in reversed order so litellm_params.metadata keeps overriding
metadata, matching the pre-refactor merge precedence.

Client bypass stays blocked. Restoring the field re-enrolls it in the
auth layer's _BANNED_REQUEST_BODY_PARAMS (derived from
_supported_callback_params via _build_banned_observability_params), so
client submissions at the top level, inside metadata, or inside a
JSON-string litellm_metadata all 401 at ingress. is_request_body_safe
also now descends into litellm_params.metadata for the same 401 defense
against the nested-body attack vector, matching how the metadata and
litellm_metadata slots are handled. _strip_client_message_redaction_opt_out
runs after the litellm_metadata JSON parse and before the admin callback_vars
unpack, so admin values survive while any leftover client-supplied
opt-out is dropped when global redaction is on and the key or team
lacks allow_client_message_redaction_opt_out.

Flip the two dynamic-param e2e tests added by the security fix to
reflect the restored override behavior, keeping the invariant that
proxy client bypass is stopped by the auth layer 401 above.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
@yucheng-berri
yucheng-berri force-pushed the litellm_key_msg_redaction branch from 6535cd1 to 8d9fbef Compare July 2, 2026 01:43
@yucheng-berri

Copy link
Copy Markdown
Contributor Author

@greptileai simplified the extractor per the reviewer note. Instead of reversed(tuple(iter_client_callback_metadata_dicts(kwargs))) the iterator now yields in priority order directly (litellm_params.metadata first, then litellm_metadata, then metadata) and the extractor consumes it in yielded order. Same effective precedence (LPM > LM > M), no wrap-and-reverse. All 435 tests still pass; test_litellm_params_metadata_overrides_metadata is still mutation-checked.

@yucheng-berri

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 8d9fbef. Configure here.

@yucheng-berri
yucheng-berri merged commit 8e6098a into litellm_internal_staging Jul 2, 2026
125 checks passed
@yucheng-berri
yucheng-berri deleted the litellm_key_msg_redaction branch July 2, 2026 22:02
yuneng-berri pushed a commit that referenced this pull request Aug 8, 2026
…ging override (LIT-3587) (#31905)

The security fix in 34e9be1 removed turn_off_message_logging from
_supported_callback_params to stop callers bypassing global redaction via
the request body. That also killed the documented admin-only per-key or
per-team override because both flows resolve through the same allowlist
in initialize_standard_callback_dynamic_params.

Put turn_off_message_logging back in _supported_callback_params so an
admin-configured metadata.logging[].callback_vars.turn_off_message_logging
survives into StandardCallbackDynamicParams and can override the global
setting for that key or team, as documented at
docs/proxy/team_logging#disableenable-message-redaction.

Consolidate the metadata traversal so the extractor and the proxy strip
walk the same set of client-controllable slots. iter_client_callback_metadata_dicts
in litellm_core_utils/initialize_dynamic_callback_params.py is the single
source of truth for metadata, litellm_metadata, and litellm_params.metadata;
_strip_client_message_redaction_opt_out imports it so a future addition
to one side automatically reaches the other. The extractor iterates the
helper in reversed order so litellm_params.metadata keeps overriding
metadata, matching the pre-refactor merge precedence.

Client bypass stays blocked. Restoring the field re-enrolls it in the
auth layer's _BANNED_REQUEST_BODY_PARAMS (derived from
_supported_callback_params via _build_banned_observability_params), so
client submissions at the top level, inside metadata, or inside a
JSON-string litellm_metadata all 401 at ingress. is_request_body_safe
also now descends into litellm_params.metadata for the same 401 defense
against the nested-body attack vector, matching how the metadata and
litellm_metadata slots are handled. _strip_client_message_redaction_opt_out
runs after the litellm_metadata JSON parse and before the admin callback_vars
unpack, so admin values survive while any leftover client-supplied
opt-out is dropped when global redaction is on and the key or team
lacks allow_client_message_redaction_opt_out.

Flip the two dynamic-param e2e tests added by the security fix to
reflect the restored override behavior, keeping the invariant that
proxy client bypass is stopped by the auth layer 401 above.

Co-authored-by: yucheng <yucheng@yuchengs-MBP.attlocal.net>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
(cherry picked from commit 8e6098a)
yuneng-berri added a commit that referenced this pull request Aug 8, 2026
…x-0808sec

chore(release): backport #30585, #30867, #31905, #32093, #32405, #34189, #36011 to stable/1.89.x and cut 1.89.7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants