Aug 5, 2026·5 min read·20 visits
Open WebUI versions <= 0.10.2 fail to strip sensitive tool source code during serialization due to a permissive Pydantic schema subclass and a non-functional database column defer flag, exposing credentials to unprivileged users.
An information disclosure vulnerability in Open WebUI versions 0.10.2 and earlier allows authenticated non-admin users with read-only access (or any authenticated user when a tool is shared publicly) to retrieve the raw Python source code of custom workspace tools. Because these server-side tools commonly contain hardcoded API tokens, credentials, and proprietary logic, the exposure of raw tool source code severely compromises confidentiality and can facilitate wider infrastructure compromise.
Open WebUI is an extensible, self-hosted AI user interface that allows users to create and execute custom server-side "tools" written in Python. These tools extend the functional capabilities of the underlying large language models. Because these tools run directly on the server, authors frequently hardcode sensitive parameters such as external service API keys, database credentials, internal service endpoints, and proprietary algorithms into the Python code.
The system is designed to enforce a strict boundary between users who can edit a tool and users who can only use or execute a tool. Standard users who have been granted read-only access to a shared tool are intended to view only the tool's functional interface specification (its schemas and parameter inputs), not the underlying backend Python source code.
Due to serialization flaws and defective database fetching mechanisms, Open WebUI versions 0.10.2 and earlier fail to enforce this write-only boundary. An authenticated user possessing minimal read access can query the tool API endpoints to retrieve the complete, plain-text Python source code, leading to an unauthorized exposure of sensitive credentials and system logic (CWE-200).
The root cause of this vulnerability lies in the combination of permissive response model schema configurations and an inactive database optimization flag. Inside backend/open_webui/routers/tools.py and backend/open_webui/models/tools.py, Open WebUI defines several Pydantic models for data validation and serialization.
While the base ToolResponse model does not include the sensitive content field (which stores the Python code), the subclass model ToolUserResponse (and its derivative ToolAccessResponse) was configured with a permissive configuration rule, effectively allowing extra fields during parsing:
model_config = ConfigDict(extra='allow')When FastAPI processes a response, it dumps the database object and unpacks the dictionary into the response model constructor. Because the subclass explicitly allowed extra fields, the unpacked 'content' key—containing the full source code—was accepted and serialized into the final HTTP response.
Furthermore, the database query layer in Tools.get_tools featured a programmatic defect. A defer_content flag was intended to instruct the SQLAlchemy ORM to omit the content column during SELECT queries. However, this flag was a complete 'no-op' and did not modify the SQL execution plan. As a result, the backend always retrieved the raw source code from the database into the execution context, enabling the permissive serialization schemas to expose it.
The vulnerability was addressed in commit c05de13b4fca1ac8a17153782b46b3d0aacf491c by restructuring how database records are serialized and validating write privileges explicitly before constructing response payloads.
Prior to the patch, the get_tools_by_id endpoint constructed the response object directly from the database model dump without evaluating whether the user should receive the raw source:
# VULNERABLE APPROACH
return ToolAccessResponse(
**tools.model_dump(),
write_access=(
(user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL)
or user.id == tools.user_id
or await AccessGrants.has_access(
user_id=user.id,
resource_type='tool',
resource_id=tools.id,
permission='write',
db=db,
)
),
)Because ToolAccessResponse allows extra fields via inheritance, the unstripped database dump passed the sensitive content field straight through to the client.
The patched code implements programmatic stripping of the content key when write_access is false, overriding the passive Pydantic model structure:
# PATCHED APPROACH
write_access = (
(user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL)
or user.id == tools.user_id
or await AccessGrants.has_access(
user_id=user.id,
resource_type='tool',
resource_id=tools.id,
permission='write',
db=db,
)
)
data = tools.model_dump()
if not write_access:
# extra='allow' re-admits content from model_dump; source is writer-only
data.pop('content', None)
return ToolAccessResponse(**data, write_access=write_access)Additionally, related fixes in Tools.get_tools repaired the defer_content parameter, ensuring that the database itself does not return the code column for standard index queries, providing a defense-in-depth barrier.
An authenticated attacker with a low-privilege user account can exploit this flaw using standard HTTP client tools. The only prerequisite is that the target tool must either be shared globally (assigned to the principal * with a read permission) or explicitly shared with the attacker's user identifier.
First, the attacker logs in to acquire a valid JSON Web Token (JWT). To retrieve the full Python source code of all available tools, the attacker sends a standard GET request to the tool listing endpoint:
GET /api/v1/tools/list HTTP/1.1
Host: target.openwebui.internal:8080
Authorization: Bearer <USER_JWT_TOKEN>
Accept: application/jsonDespite the attacker lacking write privileges (resulting in `
The CVSS Base Score is evaluated at 6.5 (Medium), with a vector of CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N. Although the score is categorized as medium because it requires standard authenticated user credentials and does not directly permit data modification or service disruption, the practical operational severity is high.
In real-world configurations, server-side tools are key points of integration with corporate infrastructure. Exposing the Python source code allows standard users to extract API keys for critical language models, database connection strings, internal microservice tokens, and proprietary IP.
If the deployment uses custom scripts to interact with internal networks, attackers can utilize the discovered endpoints and parameters to map out the private corporate architecture. This exposure corresponds to MITRE ATT&CK techniques T1005 (Data from Local System) and T1552 (Unsecured Credentials), serving as a crucial initial foothold for lateral movement.
The primary remediation for this vulnerability is to upgrade Open WebUI to version 0.11.0 or newer. This release implements programmatic pruning of sensitive fields and stabilizes DB-level column deferrals.
For environments where patching is delayed, administrators must apply alternative control measures. If custom Python scripts are not vital to daily operations, disable tool compilation entirely by setting the following environment variable:
ENABLE_PLUGINS=falseAdditionally, audit existing workspace tool permissions. Ensure that tools carrying confidential credentials or proprietary operations are not shared with the general wildcard user (*).
To identify potential exploitation attempts, security teams should review web server access logs for anomalous, high-frequency requests targeting the endpoints GET /api/v1/tools/, GET /api/v1/tools/list, and GET /api/v1/tools/id/<id> from non-administrative source IPs.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Open WebUI Open WebUI | <= 0.10.2 | 0.11.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-200 |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 | 6.5 |
| EPSS Score | Not Available |
| Impact | High Confidentiality Loss |
| Exploit Status | PoC Available |
| CISA KEV Status | Not Listed |
The product exposes sensitive information to an actor that is not authorized to have access to that information.
A high-severity access control vulnerability in ToolHive CLI before v0.30.1 and ToolHive Studio before v0.38.0 allows local containerized MCP servers to bypass network isolation. This enables malicious workloads to establish TCP/IP connections to administrative and control plane endpoints exposed on the host loopback interface.
AnyCable is a real-time communication server. Prior to version 1.6.15, its Pusher-compatible REST API suffered from an authentication bypass vulnerability because it failed to verify that the request body matched the signature-validated body_md5 parameter. This allows attackers to perform replay attacks with modified body contents.
A denial-of-service vulnerability exists in AnyIO prior to version 4.14.2. Standard error streams of process-pool workers are connected to an operating system pipe that is never drained by the parent process. This allows a worker to fill the pipe buffer and deadlock indefinitely.
CVE-2026-63349 is a critical privilege-dropping bypass vulnerability in the AnyIO asynchronous framework (versions 4.14.0 and 4.14.1) on POSIX platforms. Due to a variable assignment typo, supplementary groups specified by the developer are not correctly propagated to the execution backend, resulting in subprocesses retaining the parent process's elevated supplementary group permissions.
CVE-2026-63406 is an information disclosure vulnerability in AnyCable-go prior to version 1.6.15. The built-in telemetry client is enabled by default with a hardcoded public authentication token ('secret'). This client digests highly sensitive configuration parameters and command-line arguments, including JWT secrets and RPC secrets, into a stable SHA-256 fingerprint. This fingerprint is sent over public networks, exposing those administrative secrets to offline dictionary and brute-force attacks if intercepted.
CVE-2026-84992 is a Cross-Site Scripting (XSS) vulnerability affecting md-editor-v3 before version 6.5.4. It occurs because the fenced-code block language parser directly interpolates unescaped language metadata into unquoted HTML attributes inside the custom rendering callback. This bypasses the built-in XSSPlugin which runs during the parsing phase, before rendering.