From 9a6d16849968962ddbad6bbba548f722af2fa90b Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Thu, 23 Jul 2026 18:23:05 +0200 Subject: [PATCH] fix: enforce action availability and model access on the chat action route (#27243) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chat action route loaded a Function by its raw action_id and executed its action callable after only checking that the id and the requested model existed. The model list that the client renders actions from resolves each model's actions to the active action-type Functions that are global or assigned to that model, and the action route did not mirror that resolution, so a disabled, unassigned, or wrong-type Function, or an action on a model the caller cannot access, could be reached by calling the route directly. Gate the route on the same rules the model resolution applies: the Function must be an active action, and for server-resolved models the caller must have model access and the action must be one the model actually surfaces (matched by function id, the prefix of each model actions entry, so single and sub-actions both resolve). Direct connections carry a client-supplied model the caller already owns, so the model-bound checks are scoped to non-direct calls; the active-action check always applies. Executing admin-authored Function code remains intended behaviour — this only keeps the route consistent with which actions each model exposes. Co-authored-by: komyunghan --- backend/open_webui/utils/actions.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/utils/actions.py b/backend/open_webui/utils/actions.py index 585a409b72..1249dd60cb 100644 --- a/backend/open_webui/utils/actions.py +++ b/backend/open_webui/utils/actions.py @@ -9,7 +9,7 @@ from open_webui.models.functions import Functions from open_webui.models.users import UserModel from open_webui.socket.main import get_event_call, get_event_emitter from open_webui.utils.middleware import process_tool_result -from open_webui.utils.models import get_all_models +from open_webui.utils.models import check_model_access, get_all_models from open_webui.utils.plugin import get_function_module_from_cache logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL) @@ -46,6 +46,23 @@ async def chat_action(request: Request, action_id: str, form_data: dict, user: A raise Exception('Model not found') model = models[model_id] + # Availability gate — keep this route consistent with the actions a model + # actually surfaces to the client. Executing admin-authored Function code is + # intended; this only stops a disabled, unassigned, or access-restricted + # action from being reached by calling the route with a raw action_id. + if action.type != 'action' or not action.is_active: + raise Exception(f'Action not available: {action_id}') + + # Direct connections carry a client-supplied model the caller already owns, + # so scope the model-bound checks to server-resolved models. + if not getattr(request.state, 'direct', False) and user.role != 'admin': + await check_model_access(user, model) + # model['actions'] entries are '' or '.'; + # the function id is always the prefix. + surfaced_action_ids = {item.get('id', '').split('.', 1)[0] for item in model.get('actions', [])} + if action_id not in surfaced_action_ids: + raise Exception(f'Action not available: {action_id}') + __event_emitter__ = await get_event_emitter( { 'chat_id': data['chat_id'],