Back to skill

Security audit

Open WebUI

Security checks for vulnerabilities and agentic risk

Overview

This is a mostly coherent Open WebUI API client, but it needs Review because a remote HTTP configuration can expose the user's API token and uploaded content.

Install only if you trust the configured Open WebUI server and can use localhost or HTTPS. Avoid remote http:// URLs, avoid passing tokens on the command line on shared systems, review files before uploading them for RAG, and treat model deletion and large model pulls as destructive or costly actions that should be explicitly requested.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/openwebui-cli.py:35
Finding
Bearer Token and Sensitive User Data Can Be Transmitted over Plaintext HTTP<![CDATA[ ## Vulnerability Details **File Location**: `scripts/openwebui-cli.py:35-65`, `scripts/openwebui-cli.py:124-136` **Vulnerability Type**: Plaintext transmission of credentials and sensitive data **Risk Level**: High ### Vulnerable Code ```python def __init__(self, base_url: Optional[str] = None, token: Optional[str] = None): self.base_url = (base_url or os.getenv("OPENWEBUI_URL", "http://localhost:3000")).rstrip("/") self.token = token or os.getenv("OPENWEBUI_TOKEN") if not self.token: raise ValueError("API token required. Set OPENWEBUI_TOKEN or use --token") self.session = requests.Session() self.session.headers.update({ "Authorization": f"Bearer {self.token}", "Content-Type": "application/json" }) # Allow insecure transport for local development (localhost) if self.base_url.startswith("http://localhost") or self.base_url.startswith("http://127.0.0.1"): import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) def _url(self, endpoint: str) -> str: """Build full URL from endpoint.""" return urljoin(self.base_url + "/", endpoint.lstrip("/")) def _request(self, method: str, endpoint: str, **kwargs) -> dict: """Execute HTTP request with error handling.""" url = self._url(endpoint) verify = not (self.base_url.startswith("http://localhost") or self.base_url.startswith("http://127.0.0.1")) try: response = self.session.request(method, url, verify=verify, **kwargs) response.raise_for_status() return response.json() if response.content else {} ``` The file-upload path independently sends the same bearer credential and selected file contents: ```python def upload_file(self, file_path: str, process: bool = True) -> dict: """POST /api/v1/files/ - Upload file for RAG.""" path = Path(file_path) if not path.exists(): raise FileNotFoundError(f"File not found: {file_path}") ...[truncated 3542 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse the configured URL with `urllib.parse.urlparse` before creating the session or sending any request. 2. Require `https` for all non-loopback destinations. 3. Permit plaintext HTTP only when the normalized hostname is exactly `localhost`, `127.0.0.1`, or `::1`. 4. Reject URLs containing unexpected credentials, unsupported schemes, or missing hostnames. 5. Replace string-prefix trust checks with parsed hostname and scheme comparisons. 6. If remote plaintext HTTP must be supported for a constrained development environment, require an explicit option such as `--allow-insecure-http`, display a prominent warning, and keep it disabled by default. 7. Centralize all network requests, including multipart uploads, through the configured `requests.Session` so transport policy is applied consistently. 8. Set explicit connection and read timeouts and consider disabling redirects or revalidating the scheme and host after every redirect to prevent credentials from being forwarded to an unintended destination. 9. Avoid accepting API tokens through command-line arguments where practical because command-line values may be visible in process listings or shell history; prefer environment variables, protected configuration files, or standard input. 10. Add tests verifying that remote `http://` URLs and deceptive hostnames such as `localhost.example.com` are rejected. A secure validation pattern would resemble: ```python import ipaddress from urllib.parse import urlparse parsed = urlparse(self.base_url) if parsed.scheme not in ("http", "https") or not parsed.hostname: raise ValueError("OPENWEBUI_URL must be a valid HTTP(S) URL") hostname = parsed.hostname.lower() is_loopback = hostname == "localhost" if not is_loopback: try: is_loopback = ipaddress.ip_address(hostname).is_loopback except ValueError: pass if parsed.scheme != "https" and not is_loopback: raise ValueError("HTTPS is required for non-loopback O ...[truncated 31 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The code substantially matches much of the declared Open WebUI integration: it uses the stated environment variables or explicit arguments, interacts with Open WebUI REST endpoints, supports listing models, chatting, uploading files for RAG, managing knowledge collections, and executing Ollama proxy commands. However, the description explicitly claims support for image generation, audio processing, and pipelines, and no corresponding code paths or API methods exist for those functions. This is a description-behavior mismatch due to materially overclaiming capabilities, even though the implemented subset is otherwise aligned.

Context Leakage

High
Category
Data Exfiltration
Content
**Activate this skill when the user wants to:**
- List available models from their Open WebUI instance
- Send chat completions to models through Open WebUI
- Upload files for RAG (Retrieval Augmented Generation)
- Manage knowledge collections and add files to them
- Use Ollama proxy endpoints (generate, embed, pull models)
Confidence
90% confidence
Finding
The skill is designed to send prompts, files, and possibly conversation content to an external Open WebUI instance, which creates a real context-leakage risk if sensitive user or system data is forwarded without strict gating. This is more dangerous here because the skill explicitly supports chat completions, RAG uploads, audio, and image operations, all of which can transmit private data off-box.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### Confirmation Required

Always confirm before:
- **Deleting models** (`DELETE /ollama/api/delete`) - Irreversible
- **Pulling large models** - May take significant time/bandwidth
- **Deleting knowledge collections** - Data loss risk
- **Uploading sensitive files** - Privacy consideration
Confidence
88% confidence
Finding
The skill exposes destructive and high-impact API operations such as model deletion and knowledge-base modification, and the only guard described is a confirmation prompt. If confirmation is bypassed, spoofed, or poorly implemented, the agent could perform irreversible remote actions or incur large bandwidth and storage costs through model pulls.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
return self._request("POST", "/ollama/api/pull", json=payload)

    def ollama_delete(self, model: str) -> dict:
        """DELETE /ollama/api/delete - Delete model."""
        payload = {"name": model}
        return self._request("DELETE", "/ollama/api/delete", json=payload)
Confidence
90% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

External Model or Provider Selection

High
Category
Excessive Agency
Content
Examples:
  %(prog)s models list
  %(prog)s chat --model llama3.2 --message "Hello"
  %(prog)s files upload --file doc.pdf
"""
    )
Confidence
90% confidence
Finding
Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill documents access to environment variables and outbound network use, but it does not declare an explicit tool scope such as permissions or allowed-tools. That creates an authorization gap where a runtime may permit broader access than reviewers or users expect, especially since the skill can use API credentials and interact with remote services.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The CLI prints that it is uploading a file, but it does not clearly warn the user that the file's contents will be transmitted to the configured Open WebUI instance for storage and processing. Because this code handles potentially sensitive local files and sends them over the network, a more explicit disclosure is warranted.

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Content
elif args.command == "files":
            if args.subcommand == "upload":
                print(f"Uploading {args.file}...")
                result = client.upload_file(args.file, process=not args.no_process)
                print_json(result)
                file_id = result.get("id")
                if args.wait and not args.no_process:
Confidence
55% confidence
Finding
Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Static analysis

No suspicious patterns detected.