VAITP Dataset

← Back to the dataset

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 ::