fix: validate regeneration routes at executor boundary

This commit is contained in:
Patrick Ribbsaeter 2026-08-11 18:41:45 +02:00
parent dfcf1ef164
commit fe74cefba4
2 changed files with 34 additions and 9 deletions

View file

@ -2067,7 +2067,7 @@ async def prepare_key_update_data(
data_json: Final[dict] = data.model_dump(exclude_unset=True)
if "key_type" in data.model_fields_set:
handle_key_type(data, data_json)
if data.key_type == LiteLLMKeyType.DEFAULT and "allowed_routes" not in data.model_fields_set:
if data.key_type == LiteLLMKeyType.DEFAULT and data_json.get("allowed_routes") is None:
data_json["allowed_routes"] = []
data_json.pop("key", None)
data_json.pop("new_key", None)
@ -4817,6 +4817,11 @@ async def _execute_virtual_key_regeneration(
existing_key_row=key_in_db,
user_api_key_dict=user_api_key_dict,
)
_check_key_type_transition_against_existing_routes(
data=data,
existing_key_row=key_in_db,
user_api_key_dict=user_api_key_dict,
)
# Apply the same membership rule used on /key/update: when the caller
# asks to point the regenerated key at a different organization_id,
# require they are a member of (or proxy admin over) the target org.

View file

@ -11437,7 +11437,7 @@ async def test_regenerate_rejects_route_transition_before_recording_deletion():
execute_mock,
),
):
with pytest.raises(HTTPException) as exc_info:
with pytest.raises(ProxyException) as exc_info:
await regenerate_key_fn(
key="sk-old",
data=RegenerateKeyRequest(key_type=LiteLLMKeyType.LLM_API),
@ -11447,8 +11447,8 @@ async def test_regenerate_rejects_route_transition_before_recording_deletion():
),
)
assert exc_info.value.status_code == 403
assert "allowed_routes" in str(exc_info.value.detail)
assert exc_info.value.code == "403"
assert "allowed_routes" in str(exc_info.value.message)
persist_deleted_mock.assert_not_awaited()
execute_mock.assert_not_awaited()
@ -12145,7 +12145,30 @@ class TestAllowedRoutesCallerPermission:
assert "allowed_routes" not in update_data
@pytest.mark.asyncio
async def test_update_key_default_type_clears_existing_preset_routes(self):
@pytest.mark.parametrize(
"data",
[
pytest.param(
UpdateKeyRequest(
key="sk-test",
key_type=LiteLLMKeyType.DEFAULT,
allowed_routes=None,
),
id="explicit-null",
),
pytest.param(
UpdateKeyRequest(
key="sk-test",
key_type=LiteLLMKeyType.DEFAULT,
),
id="omitted",
),
],
)
async def test_update_key_default_type_clears_existing_preset_routes(
self,
data: UpdateKeyRequest,
):
"""An explicit default preset must remove the stored route restriction."""
from types import SimpleNamespace
@ -12154,10 +12177,7 @@ class TestAllowedRoutesCallerPermission:
)
update_data = await prepare_key_update_data(
data=UpdateKeyRequest(
key="sk-test",
key_type=LiteLLMKeyType.DEFAULT,
),
data=data,
existing_key_row=SimpleNamespace(
metadata={},
team_id=None,