CVE-2026-70491
Open WebUI discloses tool source code containing secrets to non-admin users.
- CVSS 6.5
- 200
- Authentication, Authorization, and Session Management
- Remote
Open WebUI is an extensible, feature-rich, and user-friendly self-hosted AI platform. In 0.10.2 and earlier, the GET /api/v1/tools/, GET /api/v1/tools/list, and GET /api/v1/tools/id/{id} endpoints in backend/open_webui/routers/tools.py returned full Python tool source to authenticated non-admin read-only users. ToolResponse deliberately omitted source and specs, but ToolUserResponse permitted extra fields and handlers spread a full tool model dump into the response, re-admitting omitted fields. A non-admin with a read grant can obtain another user's server-side tool source, which commonly embeds hard-coded API keys, credentials, and internal service URLs. This issue is fixed in 0.11.0.
- CWE
- 200
- CVSS base score
- 6.5
- Published
- 2026-08-04
- OWASP
- A01 Broken Access Control
- Orthogonal defect classification
- Checking
- Code defect classification
- Serialization Issues
- Category
- Authentication, Authorization, and Session Management
- Subcategory
- Insecure Handling of Sensitive Data
- Accessibility scope
- Remote
- Impact
- Information Disclosure
- Affected component
- Open WebUI
- Fixed by upgrading
- Yes
Solution
Upgrade Open WebUI to version 0.11.0 or later.
Vulnerable code sample
from pydantic import BaseModel, ConfigDict
from typing import Optional, Dict, Any
# Simplified model representing the full tool data in the database
class Tool(BaseModel):
id: str
name: str
source: str # Sensitive source code
specs: Dict[str, Any] # Potentially sensitive specs
# The response model that is intended to be safe, but is misused.
class ToolUserResponse(BaseModel):
id: str
name: str
# 'source' and 'specs' are intentionally omitted for security.
model_config = ConfigDict(extra="allow")
# A mock database function
def get_tool_from_db(tool_id: str) -> Optional[Tool]:
if tool_id == "example_tool":
return Tool(
id="example_tool",
name="Example Data Connector",
source="API_KEY = 'secret-key-12345'\n# ...",
specs={"url": "https://internal.service/api"}
)
return None
# Simplified router function for GET /api/v1/tools/id/{id}
def get_tool_by_id(tool_id: str) -> Optional[ToolUserResponse]:
tool = get_tool_from_db(tool_id)
if not tool:
return None
# VULNERABLE: The full internal model is spread into the response model.
# `extra="allow"` on ToolUserResponse permits 'source' and 'specs' to leak.
return ToolUserResponse(**tool.model_dump())Patched code sample
from pydantic import BaseModel, ConfigDict
from typing import Optional, Dict, Any
# Simplified model representing the full tool data in the database
class Tool(BaseModel):
id: str
name: str
source: str # Sensitive source code
specs: Dict[str, Any] # Potentially sensitive specs
# The response model that is intended to be safe.
class ToolUserResponse(BaseModel):
id: str
name: str
# 'source' and 'specs' are intentionally omitted for security.
model_config = ConfigDict(extra="allow")
# A mock database function
def get_tool_from_db(tool_id: str) -> Optional[Tool]:
if tool_id == "example_tool":
return Tool(
id="example_tool",
name="Example Data Connector",
source="API_KEY = 'secret-key-12345'\n# ...",
specs={"url": "https://internal.service/api"}
)
return None
# Simplified router function for GET /api/v1/tools/id/{id}
def get_tool_by_id(tool_id: str) -> Optional[ToolUserResponse]:
tool = get_tool_from_db(tool_id)
if not tool:
return None
# FIX: Explicitly construct the response model with only the intended fields,
# preventing sensitive data from the internal tool model from being exposed.
return ToolUserResponse(id=tool.id, name=tool.name)Cite this entry
@misc{vaitp:cve202670491,
title = {{Open WebUI discloses tool source code containing secrets to non-admin users.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-70491},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-70491/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
