Enforce per-model access on arena fallback before bypass_filter dispatch (#26046)

generate_chat_completion() checks model access on line 200 only when bypass_filter is
False. When an arena model reaches this function without a pre-resolved selected_model_id,
which is the task and background path (the /api/v1/tasks/* endpoints call
generate_chat_completion directly rather than through process_chat_payload), the fallback
resolves the arena to an underlying model and recurses with bypass_filter=True, so the
resolved model's access check is skipped. An authenticated user with access to an arena
could therefore reach a model they are denied directly, and for the default or exclude
arena, whose candidate pool is every non-arena model, any model on the instance (CWE-862).
The normal chat path resolves the arena in process_chat_payload before this function, so its
resolved model is checked on line 200; the task path was not, which is the inconsistency.

Enforce check_model_access() on the resolved model in the fallback, before the
bypass_filter=True recursion, mirroring the normal-path check. Admins and already-bypassed
recursive calls are unaffected, and legitimate arena use of accessible models is unchanged.

Co-authored-by: rexpository <30176934+rexpository@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Classic298
2026-06-16 22:45:42 +02:00
committed by GitHub
co-authored by rexpository Claude Opus 4.8
parent 920b655f46
commit dc4924b66e
+6
View File
@@ -237,6 +237,12 @@ async def generate_chat_completion(
form_data['model'] = selected_model_id
# bypass_filter recursion below skips the line-200 check; gate the resolved model here.
if not bypass_filter and user.role == 'user':
selected_model = request.app.state.MODELS.get(selected_model_id)
if selected_model:
await check_model_access(user, selected_model)
if selected_model_id:
if form_data.get('stream') == True: