[GH-ISSUE #22217] issue: Users can access any uploaded file by using tools #35192

Closed
opened 2026-04-25 09:25:50 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @tmcavoy35 on GitHub (Mar 4, 2026).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/22217

Check Existing Issues

  • I have searched for any existing and/or related issues.
  • I have searched for any existing and/or related discussions.
  • I have also searched in the CLOSED issues AND CLOSED discussions and found no related items (your issue might already be addressed on the development branch!).
  • I am using the latest version of Open WebUI.

Installation Method

Git Clone

Open WebUI Version

v0.8.8

Ollama Version (if applicable)

No response

Operating System

Ubuntu 22.04

Browser (if applicable)

No response

Confirmation

  • I have read and followed all instructions in README.md.
  • I am using the latest version of both Open WebUI and Ollama.
  • I have included the browser console logs.
  • I have included the Docker container logs.
  • I have provided every relevant configuration, setting, and environment variable used in my setup.
  • I have clearly listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc).
  • I have documented step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation. My steps:
  • Start with the initial platform/version/OS and dependencies used,
  • Specify exact install/launch/configure commands,
  • List URLs visited, user input (incl. example values/emails/passwords if needed),
  • Describe all options and toggles enabled or changed,
  • Include any files or environmental changes,
  • Identify the expected and actual result at each stage,
  • Ensure any reasonably skilled user can follow and hit the same issue.

Expected Behavior

Users who don't have the necessary permissions, should not be able to access files uploaded by other users.

Actual Behavior

Users can utilise tools to list all files in open-webui's upload directory and then have the model dump the contents of that file into the chat window. This allows a general User to be able to access documents that have been uploaded by other users.

Steps to Reproduce

The way I did it (could probably be done simpler) but using 2 tools. The first one retrieves a list of the files that are in the upload directory of open-webui and return them in the chat window. This gives the user the necessary uuids to then using a second tool, to dump the contents of that file into the response.

Here are the tools I used for this:
find_files.py
`"""
title: List Uploads and Generated Files
description: Lists all files in the Open WebUI upload folder and the generated output folder (/tmp).
version: 1.0
"""

import os

class Tools:
def init(self):
self.upload_dir = "/app/backend/data/uploads"
self.output_dir = "/tmp"

def _list_dir(self, path):
    if not os.path.exists(path):
        return [f"(Directory not found: {path})"]
    items = []
    for root, dirs, files in os.walk(path):
        for f in files:
            full = os.path.join(root, f)
            rel = os.path.relpath(full, path)
            items.append(rel)
    return items if items else ["(empty)"]

def list_uploads_and_outputs(self):
    uploads = self._list_dir(self.upload_dir)
    outputs = self._list_dir(self.output_dir)

    return (
        "### Uploads Directory: {}\n".format(self.upload_dir)
        + "".join(f"- {f}\n" for f in uploads)
        + "\n### Generated Files Directory: {}\n".format(self.output_dir)
        + "".join(f"- {f}\n" for f in outputs)
    )`

dump_files.py
`"""
title: Dump Uploaded File Content (PDF/DOCX/XLSX/PPTX/Images)
description: Reads a file from uploads and outputs its text content (supports PDF, DOCX, XLSX, PPTX, images via OCR).
requirements: pymupdf, python-docx, openpyxl, python-pptx, pillow, pytesseract
version: 1.1
"""

import os
import fitz # PDF
from docx import Document
from openpyxl import load_workbook
from pptx import Presentation
from PIL import Image
import pytesseract

class Tools:
def init(self):
self.upload_dir = "/app/backend/data/uploads"
self.max_chars = 20000 # safety limit

def _find_file(self, filename):
    exact = os.path.join(self.upload_dir, filename)
    if os.path.exists(exact):
        return exact

    for f in os.listdir(self.upload_dir):
        if filename in f:
            return os.path.join(self.upload_dir, f)
    return None

def _read_pdf(self, path):
    text = []
    doc = fitz.open(path)
    for page in doc:
        text.append(page.get_text())
    doc.close()
    return "\n".join(text)

def _read_docx(self, path):
    doc = Document(path)
    return "\n".join(p.text for p in doc.paragraphs)

def _read_xlsx(self, path):
    wb = load_workbook(path, data_only=True)
    output = []
    for sheet in wb.worksheets:
        output.append(f"--- Sheet: {sheet.title} ---")
        for row in sheet.iter_rows(values_only=True):
            row_text = [str(cell) if cell is not None else "" for cell in row]
            output.append("\t".join(row_text))
    return "\n".join(output)

def _read_pptx(self, path):
    prs = Presentation(path)
    output = []
    for i, slide in enumerate(prs.slides, 1):
        output.append(f"--- Slide {i} ---")
        for shape in slide.shapes:
            if hasattr(shape, "text"):
                output.append(shape.text)
    return "\n".join(output)

def _read_image(self, path):
    img = Image.open(path)
    return pytesseract.image_to_string(img)

def dump_uploaded_file(self, filename: str):
    file_path = self._find_file(filename)
    if not file_path:
        return f"File not found in uploads: {filename}"

    ext = os.path.splitext(file_path)[1].lower()

    try:
        if ext == ".pdf":
            content = self._read_pdf(file_path)
        elif ext == ".docx":
            content = self._read_docx(file_path)
        elif ext in [".xlsx", ".xlsm", ".xls"]:
            content = self._read_xlsx(file_path)
        elif ext in [".pptx", ".ppt"]:
            content = self._read_pptx(file_path)
        elif ext in [".png", ".jpg", ".jpeg", ".tif", ".tiff", ".bmp"]:
            content = self._read_image(file_path)
        else:
            with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
                content = f.read()
    except Exception as e:
        return f"Error reading file: {e}"

    if len(content) > self.max_chars:
        content = content[: self.max_chars] + "\n\n[TRUNCATED]"

    return f"### Contents of {os.path.basename(file_path)}\n\n{content}"`

Logs & Screenshots

Image Image

Additional Information

No response

Originally created by @tmcavoy35 on GitHub (Mar 4, 2026). Original GitHub issue: https://github.com/open-webui/open-webui/issues/22217 ### Check Existing Issues - [x] I have searched for any existing and/or related issues. - [x] I have searched for any existing and/or related discussions. - [x] I have also searched in the CLOSED issues AND CLOSED discussions and found no related items (your issue might already be addressed on the development branch!). - [x] I am using the latest version of Open WebUI. ### Installation Method Git Clone ### Open WebUI Version v0.8.8 ### Ollama Version (if applicable) _No response_ ### Operating System Ubuntu 22.04 ### Browser (if applicable) _No response_ ### Confirmation - [x] I have read and followed all instructions in `README.md`. - [x] I am using the latest version of **both** Open WebUI and Ollama. - [x] I have included the browser console logs. - [x] I have included the Docker container logs. - [x] I have **provided every relevant configuration, setting, and environment variable used in my setup.** - [x] I have clearly **listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup** (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc). - [x] I have documented **step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation**. My steps: - Start with the initial platform/version/OS and dependencies used, - Specify exact install/launch/configure commands, - List URLs visited, user input (incl. example values/emails/passwords if needed), - Describe all options and toggles enabled or changed, - Include any files or environmental changes, - Identify the expected and actual result at each stage, - Ensure any reasonably skilled user can follow and hit the same issue. ### Expected Behavior Users who don't have the necessary permissions, should not be able to access files uploaded by other users. ### Actual Behavior Users can utilise tools to list all files in open-webui's upload directory and then have the model dump the contents of that file into the chat window. This allows a general User to be able to access documents that have been uploaded by other users. ### Steps to Reproduce The way I did it (could probably be done simpler) but using 2 tools. The first one retrieves a list of the files that are in the upload directory of open-webui and return them in the chat window. This gives the user the necessary uuids to then using a second tool, to dump the contents of that file into the response. Here are the tools I used for this: find_files.py `""" title: List Uploads and Generated Files description: Lists all files in the Open WebUI upload folder and the generated output folder (/tmp). version: 1.0 """ import os class Tools: def __init__(self): self.upload_dir = "/app/backend/data/uploads" self.output_dir = "/tmp" def _list_dir(self, path): if not os.path.exists(path): return [f"(Directory not found: {path})"] items = [] for root, dirs, files in os.walk(path): for f in files: full = os.path.join(root, f) rel = os.path.relpath(full, path) items.append(rel) return items if items else ["(empty)"] def list_uploads_and_outputs(self): uploads = self._list_dir(self.upload_dir) outputs = self._list_dir(self.output_dir) return ( "### Uploads Directory: {}\n".format(self.upload_dir) + "".join(f"- {f}\n" for f in uploads) + "\n### Generated Files Directory: {}\n".format(self.output_dir) + "".join(f"- {f}\n" for f in outputs) )` dump_files.py `""" title: Dump Uploaded File Content (PDF/DOCX/XLSX/PPTX/Images) description: Reads a file from uploads and outputs its text content (supports PDF, DOCX, XLSX, PPTX, images via OCR). requirements: pymupdf, python-docx, openpyxl, python-pptx, pillow, pytesseract version: 1.1 """ import os import fitz # PDF from docx import Document from openpyxl import load_workbook from pptx import Presentation from PIL import Image import pytesseract class Tools: def __init__(self): self.upload_dir = "/app/backend/data/uploads" self.max_chars = 20000 # safety limit def _find_file(self, filename): exact = os.path.join(self.upload_dir, filename) if os.path.exists(exact): return exact for f in os.listdir(self.upload_dir): if filename in f: return os.path.join(self.upload_dir, f) return None def _read_pdf(self, path): text = [] doc = fitz.open(path) for page in doc: text.append(page.get_text()) doc.close() return "\n".join(text) def _read_docx(self, path): doc = Document(path) return "\n".join(p.text for p in doc.paragraphs) def _read_xlsx(self, path): wb = load_workbook(path, data_only=True) output = [] for sheet in wb.worksheets: output.append(f"--- Sheet: {sheet.title} ---") for row in sheet.iter_rows(values_only=True): row_text = [str(cell) if cell is not None else "" for cell in row] output.append("\t".join(row_text)) return "\n".join(output) def _read_pptx(self, path): prs = Presentation(path) output = [] for i, slide in enumerate(prs.slides, 1): output.append(f"--- Slide {i} ---") for shape in slide.shapes: if hasattr(shape, "text"): output.append(shape.text) return "\n".join(output) def _read_image(self, path): img = Image.open(path) return pytesseract.image_to_string(img) def dump_uploaded_file(self, filename: str): file_path = self._find_file(filename) if not file_path: return f"File not found in uploads: {filename}" ext = os.path.splitext(file_path)[1].lower() try: if ext == ".pdf": content = self._read_pdf(file_path) elif ext == ".docx": content = self._read_docx(file_path) elif ext in [".xlsx", ".xlsm", ".xls"]: content = self._read_xlsx(file_path) elif ext in [".pptx", ".ppt"]: content = self._read_pptx(file_path) elif ext in [".png", ".jpg", ".jpeg", ".tif", ".tiff", ".bmp"]: content = self._read_image(file_path) else: with open(file_path, "r", encoding="utf-8", errors="ignore") as f: content = f.read() except Exception as e: return f"Error reading file: {e}" if len(content) > self.max_chars: content = content[: self.max_chars] + "\n\n[TRUNCATED]" return f"### Contents of {os.path.basename(file_path)}\n\n{content}"` ### Logs & Screenshots <img width="800" height="199" alt="Image" src="https://github.com/user-attachments/assets/35de521c-8766-43a2-acd7-13cd618b0f6e" /> <img width="1410" height="361" alt="Image" src="https://github.com/user-attachments/assets/dd6ee2c4-f6c1-4034-8bfb-ff9c1b47ecfe" /> ### Additional Information _No response_
GiteaMirror added the bug label 2026-04-25 09:25:50 -05:00
Author
Owner

@Classic298 commented on GitHub (Mar 4, 2026):

Intended.

Tools are powerful plugins running inside Open WebUI.

They are just python code that runs on your machines.

Normal users should not be given access to tools - henceforth this is also off by default - and the documentation explicitly warns against giving normal users access to tools to allow them to create their own tools since it's basically arbitrary code execution.

Users with access to tools need to be trusted users (i.e. admins or special developers in a group which has permissions, but nobody else).

(FYI tools can do a lot worse things in the wrong hands besides reading files in the uploads directory. Hence: only give access to tools to trusted users. Tools are PLUGINS!)

Read the docs.

<!-- gh-comment-id:3997442152 --> @Classic298 commented on GitHub (Mar 4, 2026): Intended. Tools are powerful plugins running inside Open WebUI. They are just python code that runs on your machines. Normal users should not be given access to tools - henceforth this is also off by default - and the documentation explicitly warns against giving normal users access to tools to allow them to create their own tools since it's basically arbitrary code execution. Users with access to tools need to be trusted users (i.e. admins or special developers in a group which has permissions, but nobody else). (FYI tools can do a lot worse things in the wrong hands besides reading files in the uploads directory. Hence: only give access to tools to trusted users. Tools are PLUGINS!) **Read the docs.**
Author
Owner
<!-- gh-comment-id:3997475867 --> @Classic298 commented on GitHub (Mar 4, 2026): <img width="710" height="179" alt="Image" src="https://github.com/user-attachments/assets/67dc0c67-d45a-4c4a-89a9-61e3425f1f7b" /> https://docs.openwebui.com/features/extensibility/plugin/tools/ <img width="743" height="714" alt="Image" src="https://github.com/user-attachments/assets/134e2f92-2825-4d15-853d-03a387cfa32d" /> https://docs.openwebui.com/features/extensibility/plugin/
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#35192