From 897d69a35c65f8ab54583bb9ca8dc74eab7bcd29 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Mon, 27 Jul 2026 07:54:00 +0200 Subject: [PATCH] fix: enforce feature permissions on the legacy chat-features block (image_generation, web_search) (#26703) The legacy features block in process_chat_payload honoured client-supplied features.image_generation and features.web_search flags and dispatched to the image generation/edit provider and the web-search provider without re-checking the per-user permission that the direct /images routes and the native function-calling path enforce. A user denied features.image_generation or features.web_search could still trigger billable server-side image generation or web search via POST /api/chat/completions with params.function_calling set to legacy. Gate both branches on admin-or-has_permission before invoking chat_image_generation_handler / chat_web_search_handler, matching the existing code_interpreter gate, so a forged flag from an unpermitted user is ignored. Normal completions and permitted users are unaffected. --- backend/open_webui/utils/middleware.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index e2973d9a12..1fade157f8 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -2523,14 +2523,26 @@ async def process_chat_payload(request, form_data, user, metadata, model): form_data = await add_memory_context(request, form_data, user, model) if 'web_search' in features and features['web_search']: - # Skip forced RAG web search when native FC is enabled - model can use web_search tool - if metadata.get('params', {}).get('function_calling') == 'legacy': - form_data = await chat_web_search_handler(request, form_data, extra_params, user) + # features is client-supplied; re-check the permission the native FC path enforces. + if getattr(user, 'role', None) == 'admin' or await has_permission( + getattr(user, 'id', ''), + 'features.web_search', + await Config.get('user.permissions'), + ): + # Skip forced RAG web search when native FC is enabled - model can use web_search tool + if metadata.get('params', {}).get('function_calling') == 'legacy': + form_data = await chat_web_search_handler(request, form_data, extra_params, user) if 'image_generation' in features and features['image_generation']: - # Skip forced image generation when native FC is enabled - model can use generate_image tool - if metadata.get('params', {}).get('function_calling') == 'legacy': - form_data = await chat_image_generation_handler(request, form_data, extra_params, user) + # features is client-supplied; re-check the permission the direct /images routes enforce. + if getattr(user, 'role', None) == 'admin' or await has_permission( + getattr(user, 'id', ''), + 'features.image_generation', + await Config.get('user.permissions'), + ): + # Skip forced image generation when native FC is enabled - model can use generate_image tool + if metadata.get('params', {}).get('function_calling') == 'legacy': + form_data = await chat_image_generation_handler(request, form_data, extra_params, user) if 'code_interpreter' in features and features['code_interpreter']: engine = await Config.get('code_interpreter.engine', 'pyodide')