Skip to content

fix(proxy): gate team allowed_passthrough_routes to proxy admins#28097

Merged
yuneng-berri merged 2 commits into
litellm_internal_stagingfrom
litellm_team_passthrough_routes_gating
May 18, 2026
Merged

fix(proxy): gate team allowed_passthrough_routes to proxy admins#28097
yuneng-berri merged 2 commits into
litellm_internal_stagingfrom
litellm_team_passthrough_routes_gating

Conversation

@ryan-crabbe-berri

@ryan-crabbe-berri ryan-crabbe-berri commented May 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

allowed_passthrough_routes short-circuits the role-based route gate in RouteChecks.non_proxy_admin_allowed_routes_check, so the keys endpoints already restrict it to proxy admins. The team writers (/team/new, /team/update) had no equivalent check. An org admin — a non-proxy-admin who clears the route gate (org_admin_allowed_routes) and _verify_team_access (as org admin of the team's org) — could therefore self-grant arbitrary pass-through routes on a team in their org, an access-control field the keys path deliberately reserves for proxy admins.

This lifts the keys check into a shared _check_passthrough_routes_caller_permission helper (parametrized by entity for the error message) and applies it to both team endpoints. Behavior is now identical for keys and teams; the keys path is unchanged (same helper, default entity="key", all existing call sites intact). Both the top-level field and the metadata.allowed_passthrough_routes smuggling path are covered.

Threat framing: for org admins this closes a real privilege escalation. For plain team admins it is defense-in-depth — the route gate already default-denies them at /team/*, but the gate becomes load-bearing under configs that widen route access (e.g. a key minted with explicit allowed_routes).

Refs LIT-1901

Before / After

before
Screenshot 2026-05-16 at 4 53 42 PM
Screenshot 2026-05-16 at 4 53 54 PM
Screenshot 2026-05-16 at 4 54 23 PM

after
Screenshot 2026-05-16 at 4 54 45 PM

Test plan

  • tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py::test_check_passthrough_routes_caller_permission_team — proxy admin allowed; non-admin blocked (top-level + metadata-smuggled); empty/absent allowed so clearing stays open
  • ...::test_new_team_blocks_non_admin_passthrough_routes/team/new rejects non-proxy-admin (wrapped ProxyException code 403)
  • ...::test_update_team_blocks_non_admin_passthrough_routes/team/update rejects non-proxy-admin after _verify_team_access passes
  • Keys regression: test_key_management_endpoints.py -k passthrough passes (helper moved, call sites unchanged)
  • Full team suite green (139 passed)
  • Manual before/after via org-admin caller through the running proxy (200+persisted → 403+null)

Refs LIT-3019

allowed_passthrough_routes short-circuits the role-based route gate, so
the keys endpoints already restrict it to proxy admins. The team writers
(/team/new, /team/update) had no equivalent check, letting an org admin
(a non-proxy-admin who clears the route gate and _verify_team_access)
self-grant pass-through routes on their team. Lift the keys check into a
shared helper and apply it to both team endpoints.

Resolves LIT-3019
@codecov

codecov Bot commented May 16, 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 May 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR closes a privilege-escalation path where an org admin could call /team/new or /team/update to self-grant allowed_passthrough_routes — a field that intentionally bypasses the role-based route gate and was already restricted to proxy admins on key endpoints. The fix extracts the existing helper from key_management_endpoints.py into common_utils.py, adds an entity parameter for cleaner error messages, and calls it from both team write endpoints.

  • common_utils._check_passthrough_routes_caller_permission: moved and parameterised; guards both the direct field and the metadata.allowed_passthrough_routes smuggling path, with an empty-list pass-through that lets non-admins clear the field.
  • team_endpoints.new_team / update_team: new guard runs after access verification but before any database write, with entity="team" so error messages are accurate.
  • Tests: three new mock-only tests (unit + two integration) cover the happy path, both block paths, and edge cases; all five existing key-endpoint call sites are unaffected.

Confidence Score: 5/5

Safe to merge; the change is additive permission enforcement with no observable impact on correctly-permissioned callers.

The refactor is a clean extract-and-reuse: the helper logic is identical to the key-endpoint version, the default entity=key keeps all existing call sites behaviorally unchanged, and both new call sites in team_endpoints are placed correctly in the execution order (post-access-verify, pre-DB-write). The three new tests cover admin pass-through, both non-admin block paths, and the edge case that an empty list is not treated as a setting attempt. No existing tests were altered.

No files require special attention.

Important Files Changed

Filename Overview
litellm/proxy/management_endpoints/common_utils.py Adds shared _check_passthrough_routes_caller_permission helper (moved from key_management_endpoints.py) with a new entity keyword param (default "key") for context-aware error messages; covers both top-level and metadata-smuggled allowed_passthrough_routes.
litellm/proxy/management_endpoints/key_management_endpoints.py Removes the now-duplicated local _check_passthrough_routes_caller_permission and imports it from common_utils; all five existing call sites are unchanged and retain the default entity="key" behaviour.
litellm/proxy/management_endpoints/team_endpoints.py Inserts the permission check in new_team (after member auto-enrolment, before any DB write) and in update_team (after _verify_team_access, before the kv update); both sites block non-proxy-admins from setting allowed_passthrough_routes on a team.
tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py Adds three new unit/integration tests: unit test covers admin-allow, non-admin-block (top-level + metadata), and empty-list pass-through; integration tests exercise the full new_team and update_team code paths with mocked DB/access checks.

Reviews (2): Last reviewed commit: "docs(proxy): note view-only admins are i..." | Re-trigger Greptile

Comment thread litellm/proxy/management_endpoints/common_utils.py
…ssthrough gate

Clarifies the proxy-admin guard per review feedback; no behavior change.

Refs LIT-3019
@ryan-crabbe-berri

Copy link
Copy Markdown
Collaborator Author

@greptileai re review

@yuneng-berri yuneng-berri merged commit fe63650 into litellm_internal_staging May 18, 2026
115 checks passed
@yuneng-berri yuneng-berri deleted the litellm_team_passthrough_routes_gating branch May 18, 2026 15:56
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.

2 participants