Skip to content

chore(proxy): backport #27898 + #27801 to 1.84.0rc2#27902

Merged
yuneng-berri merged 2 commits into
litellm_1.84.0rc2from
litellm_/eager-euler-fd3639
May 14, 2026
Merged

chore(proxy): backport #27898 + #27801 to 1.84.0rc2#27902
yuneng-berri merged 2 commits into
litellm_1.84.0rc2from
litellm_/eager-euler-fd3639

Conversation

@yuneng-berri

Copy link
Copy Markdown
Collaborator

Backports two request-body / module-load hardening PRs onto litellm_1.84.0rc2.

Backports

  • #27898chore(proxy): cover extra_body + azure_ad_token in banned-params check
  • #27801chore(proxy): refuse remote-URL instance-fn loads outside config-file path

Both are squash-merged on litellm_internal_staging; this branch cherry-picks each merge commit as a single backport commit (-m 1 -x).

Conflict resolution

rc2 is 969 commits behind staging from the merge-base, but git auto-merged the three overlapping files cleanly with no manual conflict resolution:

  • litellm/proxy/proxy_server.py — line offsets only (rc2 has fewer lines above the patch site)
  • litellm/proxy/_types.py — line offsets only
  • litellm/proxy/_experimental/mcp_server/tool_registry.py — line offsets only

Verified by comparing per-file diffs git diff <pr_merge>^1..<pr_merge> (the PR's own hunk) against git diff origin/litellm_1.84.0rc2..HEAD (what we landed): byte-identical content, only line numbers differ. No staging-only features leaked in (e.g. TEAM_KEY_BULK_UPDATE, compliance_check_routes, unregister_tools_with_prefix, delegate_auth_to_upstream are correctly absent on this backport since they belong to other PRs).

Test plan

  • pytest tests/test_litellm/proxy/auth/test_banned_params_extra_body.py tests/test_litellm/proxy/types_utils/test_get_instance_fn_runtime_gate.py tests/test_litellm/proxy/types_utils/test_db_overlay_remote_module_scrub.py — 38 passed
  • pytest tests/proxy_unit_tests/test_custom_logger_s3_gcs.py — 11 passed
  • pytest tests/test_litellm/proxy/auth/ — 808 passed

…over

chore(proxy): cover extra_body + azure_ad_token in banned-params check

(cherry picked from commit a6a9d8e)
…-gate

chore(proxy): refuse remote-URL instance-fn loads outside config-file path

(cherry picked from commit e3e5209)
@yuneng-berri yuneng-berri merged commit 08ea016 into litellm_1.84.0rc2 May 14, 2026
85 of 101 checks passed
@yuneng-berri yuneng-berri deleted the litellm_/eager-euler-fd3639 branch May 14, 2026 04:14
@greptile-apps

greptile-apps Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This backport brings two security-hardening changes from litellm_internal_staging onto the litellm_1.84.0rc2 release branch: a runtime gate that refuses s3:///gcs:// remote-module loads from any call-site that lacks a config_file_path (blocking admin-endpoint-to-RCE), and a DB-overlay scrub that strips those URLs before they can merge into the in-memory config during the YAML-load cycle.

  • Remote-module gate (types_utils/utils.py, proxy_server.py): get_instance_fn now raises ValueError for s3:///gcs:// values when config_file_path is None; the parameter is threaded through the entire config-load chain (callbacks, pass-through endpoints, MCP tool registry, JWT auth). A _scrub_db_overlay_remote_module_loads function strips remote URLs from DB-overlay values at the merge boundary for all fields that flow into get_instance_fn.
  • extra_body + azure_ad_token ban (auth/auth_utils.py): extra_body is added to _NESTED_CONFIG_KEYS with _coerce_metadata_to_dict coercion (handling JSON-stringified variants), and azure_ad_token joins the banned-param list, closing the same credential-exfil shape as aws_web_identity_token on the Azure path.

Confidence Score: 4/5

Safe to merge; both findings only manifest under adversarial PROXY_ADMIN input and do not affect normal operation.

The gate and scrub logic correctly close the documented attack vectors and all config-load paths are updated consistently. The two gaps found — double-scrub of v2 guardrail litellm_params (harmless) and unguarded target=None in pass_through route registration — are minor and do not affect normal operation.

litellm/proxy/proxy_server.py (guardrail double-scrub, pass_through target=None handling) and litellm/proxy/pass_through_endpoints/pass_through_endpoints.py (no null guard for target=None in create_pass_through_route)

Important Files Changed

Filename Overview
litellm/proxy/types_utils/utils.py Adds a runtime gate that rejects s3:// / gcs:// remote-module loads when config_file_path is None; clean and well-tested.
litellm/proxy/auth/auth_utils.py Extends banned-params descent to extra_body with _coerce_metadata_to_dict coercion and adds azure_ad_token to the ban list; symmetric with existing metadata key handling.
litellm/proxy/proxy_server.py Adds _scrub_db_overlay_remote_module_loads and threads config_file_path through the load chain; v2 guardrail litellm_params is scrubbed twice (harmless), and scrubbed target=None in pass_through_endpoints is not guarded downstream.
litellm/proxy/_types.py Pops config_file_path from kwargs in LiteLLM_JWTAuth.init before unknown-keys check, then threads it to get_instance_fn for custom_validate; clean change.
litellm/proxy/pass_through_endpoints/pass_through_endpoints.py Propagates config_file_path through create_pass_through_route and initialize_pass_through_endpoints; no null guard for target=None after scrubbing.
litellm/proxy/_experimental/mcp_server/tool_registry.py Adds config_file_path parameter to load_tools_from_config and passes it to get_instance_fn; straightforward threading change.
tests/test_litellm/proxy/auth/test_banned_params_extra_body.py New tests covering extra_body banned-param descent including stringified-JSON form and the allow_client_side_credentials escape; comprehensive.
tests/test_litellm/proxy/types_utils/test_db_overlay_remote_module_scrub.py New tests for _scrub_db_overlay_remote_module_loads covering all field shapes; mutation-safety and passthrough cases covered.
tests/test_litellm/proxy/types_utils/test_get_instance_fn_runtime_gate.py New tests verifying the get_instance_fn gate for both schemes, the config-file-path allow path, and threading through pass_through and MCP registry.
tests/proxy_unit_tests/test_custom_logger_s3_gcs.py Existing tests updated to pass config_file_path for s3:// / gcs:// URLs, preserving intent without weakening coverage.

Comments Outside Diff (2)

  1. litellm/proxy/proxy_server.py, line 3353-3366 (link)

    P2 Redundant double-scrub of v2 guardrail litellm_params

    For v2-shaped guardrail entries ({"guardrail_name": "...", "litellm_params": {...}}), _scrub_guardrail_inner is called twice on the same litellm_params dict — once in the generic for inner in entry.values() loop (because litellm_params value is a dict) and once via the explicit lp = entry.get("litellm_params") path below. The second call is a no-op (both calls mutate the same deep-copied object), but the intent is unclear at a glance. Excluding litellm_params from the generic loop would make the v1/v2 distinction explicit.

  2. litellm/proxy/proxy_server.py, line 3383-3398 (link)

    P2 Scrubbed target: None is not guarded against in downstream route registration

    When a DB-overlay pass_through_endpoints entry has its remote-URL target scrubbed to None, the entry remains in the list. The test comment notes it "can still be skipped explicitly downstream", but create_pass_through_route has no null guard: it calls get_instance_fn(value=None, ...) which hits None.startswith("s3://") and raises AttributeError. If the surrounding try block does not catch AttributeError, the config-reload cycle would fail. An explicit if target is None: return guard in _register_pass_through_endpoint or create_pass_through_route would make the intended skip behavior concrete.

Reviews (1): Last reviewed commit: "Merge pull request #27801 from stuxf/cho..." | Re-trigger Greptile

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.

1 participant