Skip to content

fix(router): constrain same-name deployment routing by access groups - #26161

Merged
Sameerlite merged 12 commits into
litellm_internal_stagingfrom
litellm_access-group-routing-fix
May 2, 2026
Merged

fix(router): constrain same-name deployment routing by access groups#26161
Sameerlite merged 12 commits into
litellm_internal_stagingfrom
litellm_access-group-routing-fix

Conversation

@Sameerlite

@Sameerlite Sameerlite commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • filter router candidate deployments by caller-authorized model access groups when access to a public model is granted via access-group membership
  • pass request_kwargs through get_available_deployment() so metadata-based filtering is applied on sync routing paths
  • add regression coverage for shared public model names across multiple access groups, plus a guard test for explicit model access behavior

Test plan

  • pytest tests/test_litellm/test_router.py -k \"access_group_scoped_key_filters_deployments_with_same_public_model or explicit_model_access_does_not_force_access_group_filtering\" -q

Fixes #18333


Note

Medium Risk
Changes core routing selection to enforce access-group-based authorization and alters fallback behavior, which could impact which deployments are chosen or when requests error. Risk is mitigated by added regression tests but affects permission-sensitive logic.

Overview
Router deployment resolution now applies access-group constraints when request metadata includes user_api_key_auth and the caller’s permissions for the requested public model are access-group-only, filtering candidate deployments (especially when multiple deployments share the same model_name).

It also prevents unauthorized bypass via routing fallbacks by (a) skipping the _get_deployment_by_litellm_model branch when access-group filtering emptied candidates and (b) blocking default fallbacks model substitution in the same scenario; sync get_available_deployment() now passes request_kwargs so this filtering runs on non-async paths.

Adds targeted tests covering access-group-scoped routing, explicit model access behavior, and regression cases ensuring neither litellm-model lookup nor default fallbacks can circumvent access-group restrictions.

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

Filter router candidate deployments by caller-authorized model access groups when access is granted via group membership, preventing cross-group load balancing for shared public model names.

Made-with: Cursor
Add a router utils unit test that directly exercises _filter_deployments_by_model_access_groups for access-group-only key permissions.

Made-with: Cursor
@Sameerlite
Sameerlite temporarily deployed to integration-postgres April 21, 2026 09:52 — with GitHub Actions Inactive
@Sameerlite
Sameerlite temporarily deployed to integration-postgres April 21, 2026 09:52 — with GitHub Actions Inactive
@greptile-apps

greptile-apps Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR tightens Router deployment selection by filtering candidates to caller-authorized access groups when a key's access to a public model name is granted only via access-group membership. It extracts the pre-existing "model not in names" routing into _try_early_resolve_deployments_for_model_not_in_names, adds the new _filter_deployments_by_model_access_groups method, threads request_kwargs through the sync get_available_deployment path, and guards both the _get_deployment_by_litellm_model fallback and the default-fallback path against bypassing the filter when access-group filtering was the reason candidates were removed.

Confidence Score: 4/5

Safe to merge with care — the main guard paths are correctly implemented, but _try_early_resolve_deployments_for_model_not_in_names still returns team/pattern/default deployments without access-group filtering (pre-existing behavior, noted in prior review round).

No new P0/P1 issues found. Previously flagged P1s (stale _access_group_filter_emptied_candidates, _get_deployment_by_litellm_model bypass, default-fallback bypass) are all addressed by the guards in this PR. The remaining unguarded path in _try_early_resolve_deployments_for_model_not_in_names is pre-existing behavior flagged as P2 in a prior review round. Conservative 4/5 reflects the security-sensitive routing change.

litellm/router.py — specifically _try_early_resolve_deployments_for_model_not_in_names, which still returns team/pattern/default deployments without access-group filtering.

Important Files Changed

Filename Overview
litellm/router.py Adds _filter_deployments_by_model_access_groups and _try_early_resolve_deployments_for_model_not_in_names; threads request_kwargs through sync routing path; guards litellm-model and default-fallback paths against access-group bypass. Core logic is sound but _try_early_resolve_deployments_for_model_not_in_names still returns team/pattern/default deployments without access-group filtering.
tests/test_litellm/router_utils/test_router_utils_common_utils.py Adds unit test for _filter_deployments_by_model_access_groups using a Router with access-group-tagged deployments; no real network calls.
tests/test_litellm/test_router.py Adds five regression tests covering access-group scoping, explicit-model bypass, litellm-model-fallback guard, and default-fallback guard; all use mock_response and make no real network calls.

Reviews (8): Last reviewed commit: "Fix access-group bypass via litellm-mode..." | Re-trigger Greptile

Comment thread litellm/router.py
Document why empty allowed_access_groups intentionally preserves unfiltered deployments to avoid breaking non-access-group authorization paths.

Made-with: Cursor
@Sameerlite
Sameerlite temporarily deployed to integration-postgres April 21, 2026 09:59 — with GitHub Actions Inactive
@Sameerlite
Sameerlite temporarily deployed to integration-postgres April 21, 2026 09:59 — with GitHub Actions Inactive
@Sameerlite
Sameerlite temporarily deployed to integration-postgres April 21, 2026 09:59 — with GitHub Actions Inactive
@Sameerlite
Sameerlite temporarily deployed to integration-postgres April 23, 2026 03:09 — with GitHub Actions Inactive
@Sameerlite
Sameerlite temporarily deployed to integration-postgres April 23, 2026 03:09 — with GitHub Actions Inactive
@Sameerlite
Sameerlite temporarily deployed to integration-postgres April 23, 2026 03:09 — with GitHub Actions Inactive
@Sameerlite
Sameerlite temporarily deployed to integration-postgres April 23, 2026 03:09 — with GitHub Actions Inactive
@Sameerlite
Sameerlite temporarily deployed to integration-postgres April 23, 2026 03:09 — with GitHub Actions Inactive
@codecov

codecov Bot commented Apr 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@yuneng-berri

Copy link
Copy Markdown
Contributor

@Sameerlite Please see if the greptile comment is applicable here

@Alluuu

Alluuu commented Apr 27, 2026

Copy link
Copy Markdown

@Sameerlite Thanks for paying attention to this issue, but is there anything I can do to help move this along?

@Sameerlite
Sameerlite temporarily deployed to integration-postgres May 1, 2026 12:26 — with GitHub Actions Inactive
@Sameerlite
Sameerlite temporarily deployed to integration-postgres May 1, 2026 12:26 — with GitHub Actions Inactive
@Sameerlite
Sameerlite temporarily deployed to integration-postgres May 1, 2026 12:26 — with GitHub Actions Inactive
@mateo-berri
mateo-berri self-requested a review May 1, 2026 17:14
@mateo-berri

Copy link
Copy Markdown
Contributor

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 e942ef6. Configure here.

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@mateo-berri
mateo-berri had a problem deploying to integration-postgres May 1, 2026 22:51 — with GitHub Actions Failure
@mateo-berri
mateo-berri had a problem deploying to integration-postgres May 1, 2026 22:51 — with GitHub Actions Failure
@mateo-berri
mateo-berri had a problem deploying to integration-postgres May 1, 2026 22:51 — with GitHub Actions Failure
@mateo-berri
mateo-berri had a problem deploying to integration-postgres May 1, 2026 22:51 — with GitHub Actions Failure
@mateo-berri
mateo-berri had a problem deploying to integration-postgres May 1, 2026 22:51 — with GitHub Actions Failure
@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

Comment thread litellm/router.py Outdated
@mateo-berri
mateo-berri temporarily deployed to integration-postgres May 1, 2026 23:03 — with GitHub Actions Inactive
@mateo-berri
mateo-berri had a problem deploying to integration-postgres May 1, 2026 23:03 — with GitHub Actions Error
@mateo-berri
mateo-berri had a problem deploying to integration-postgres May 1, 2026 23:03 — with GitHub Actions Error
@mateo-berri
mateo-berri temporarily deployed to integration-postgres May 1, 2026 23:03 — with GitHub Actions Inactive
@mateo-berri
mateo-berri had a problem deploying to integration-postgres May 1, 2026 23:03 — with GitHub Actions Error
@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

Comment thread litellm/router.py
Comment thread litellm/router.py
When _get_all_deployments returns 0 candidates and the litellm-model
fallback branch (_get_deployment_by_litellm_model) finds deployments that
the access-group filter then empties, _access_group_filter_emptied_candidates
remained False (it was captured before that branch ran). The router would
then proceed to default fallbacks; the fallback model could have no
access_groups and short-circuit the filter, silently serving a caller
blocked by access-group restrictions.

Update the flag inside the litellm-model branch when filtering empties a
non-empty candidate set so the default-fallback guard still triggers.
@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

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 9d945fe. Configure here.

@mateo-berri mateo-berri 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.

LGTM; thanks!

…ames

The router_code_coverage CI check requires every function in router.py to
be referenced by at least one test under tests/{local_testing,
router_unit_tests,test_litellm} in a file with "router" in its name.
The recently-extracted helper had no direct test, so the check failed
with "0.45% of functions in router.py are not tested".

Add a focused test that exercises the four return paths: model already
in self.model_names, no fallback applies, pattern-router match, and
default_deployment substitution (also asserting the stored default
isn't mutated).

https://claude.ai/code/session_019AVp1XL7RT9RxRe4qRLkay
@CLAassistant

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 all sign our Contributor License Agreement before we can accept your contribution.
2 out of 3 committers have signed the CLA.

✅ mateo-berri
✅ Sameerlite
❌ claude
You have signed the CLA already but the status is still pending? Let us recheck it.

@Sameerlite
Sameerlite merged commit d2015f0 into litellm_internal_staging May 2, 2026
115 checks passed
@Sameerlite
Sameerlite deleted the litellm_access-group-routing-fix branch May 2, 2026 05:49
fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
…uting-fix

fix(router): constrain same-name deployment routing by access groups
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.

[Bug]: CRITICAL - Models loadbalanced outside of Model Access Group

6 participants