From 25cf097da8cd7f9ca9d3b46ccbfe306b51dcb3c6 Mon Sep 17 00:00:00 2001 From: harish-berri Date: Wed, 29 Apr 2026 20:14:30 +0000 Subject: [PATCH] add test(tag-routing): prevent header regex bypass for strict plain tags. Add tests to validate the condition improve the conditional readability by naming the plain-tag check explicitly. --- litellm/router_strategy/tag_based_routing.py | 3 +- .../test_router_tag_routing.py | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/litellm/router_strategy/tag_based_routing.py b/litellm/router_strategy/tag_based_routing.py index 0163f3bbd4f..07143af38a2 100644 --- a/litellm/router_strategy/tag_based_routing.py +++ b/litellm/router_strategy/tag_based_routing.py @@ -106,7 +106,8 @@ def _match_deployment( # check either didn't run (no request tags) or failed (step 1 returned # None). Block the regex path so it cannot circumvent the operator's # strict-tag policy. - strict_tag_check_failed = not match_any and bool(deployment_tags) + deployment_has_plain_tags = deployment_tags is not None and len(deployment_tags) > 0 + strict_tag_check_failed = not match_any and deployment_has_plain_tags if deployment_tag_regex and header_strings and not strict_tag_check_failed: regex_match = _is_valid_deployment_tag_regex( deployment_tag_regex, header_strings diff --git a/tests/test_litellm/router_strategy/test_router_tag_routing.py b/tests/test_litellm/router_strategy/test_router_tag_routing.py index 4424c68f1d9..a6e39ec3c0a 100644 --- a/tests/test_litellm/router_strategy/test_router_tag_routing.py +++ b/tests/test_litellm/router_strategy/test_router_tag_routing.py @@ -346,6 +346,34 @@ def test_tag_routing_with_list_of_tags_match_all(): assert not is_valid_deployment_tag(["default"], ["teamA"], match_any=False) +def test_strict_tag_routing_without_request_tags_blocks_header_regex_fallback(): + """ + When tag_filtering_match_any=False, deployments with plain tags must require + those request tags before header regex can match. A spoofed User-Agent must + not route to a tagged deployment when the request has no tags. + """ + from litellm.router_strategy.tag_based_routing import _match_deployment + + deployment = { + "model_name": "restricted-model", + "litellm_params": { + "model": "gpt-4o", + "tags": ["internal"], + "tag_regex": ["^User-Agent: internal-tool"], + }, + } + + assert ( + _match_deployment( + deployment=deployment, + request_tags=None, + header_strings=["User-Agent: internal-tool"], + match_any=False, + ) + is None + ) + + @pytest.mark.asyncio() async def test_router_free_paid_tier_with_responses_api(): """