diff --git a/autogpt_platform/backend/backend/api/features/chat/tools/workspace_tools.py b/autogpt_platform/backend/backend/api/features/chat/tools/workspace_tools.py index d9d12042d5..a435ff04a9 100644 --- a/autogpt_platform/backend/backend/api/features/chat/tools/workspace_tools.py +++ b/autogpt_platform/backend/backend/api/features/chat/tools/workspace_tools.py @@ -10,7 +10,8 @@ from pydantic import BaseModel from backend.api.features.chat.model import ChatSession from backend.data.workspace import get_or_create_workspace from backend.util.virus_scanner import scan_content_safe -from backend.util.workspace import MAX_FILE_SIZE_BYTES, WorkspaceManager +from backend.util.settings import Config +from backend.util.workspace import WorkspaceManager from .base import BaseTool from .models import ErrorResponse, ResponseType, ToolResponseBase @@ -468,9 +469,10 @@ class WriteWorkspaceFileTool(BaseTool): ) # Check size - if len(content) > MAX_FILE_SIZE_BYTES: + max_file_size = Config().max_file_size_mb * 1024 * 1024 + if len(content) > max_file_size: return ErrorResponse( - message=f"File too large. Maximum size is {MAX_FILE_SIZE_BYTES // (1024*1024)}MB", + message=f"File too large. Maximum size is {Config().max_file_size_mb}MB", session_id=session_id, ) diff --git a/autogpt_platform/backend/backend/data/workspace.py b/autogpt_platform/backend/backend/data/workspace.py index a354d3a3c3..1c439e2786 100644 --- a/autogpt_platform/backend/backend/data/workspace.py +++ b/autogpt_platform/backend/backend/data/workspace.py @@ -55,6 +55,7 @@ async def get_workspace(user_id: str) -> Optional[UserWorkspace]: async def create_workspace_file( workspace_id: str, + file_id: str, name: str, path: str, storage_path: str, @@ -71,6 +72,7 @@ async def create_workspace_file( Args: workspace_id: The workspace ID + file_id: The file ID (same as used in storage path for consistency) name: User-visible filename path: Virtual path (e.g., "/documents/report.pdf") storage_path: Actual storage path (GCS or local) @@ -91,6 +93,7 @@ async def create_workspace_file( file = await UserWorkspaceFile.prisma().create( data={ + "id": file_id, "workspaceId": workspace_id, "name": name, "path": path, diff --git a/autogpt_platform/backend/backend/util/file.py b/autogpt_platform/backend/backend/util/file.py index 538fe59df6..42de0fa6c2 100644 --- a/autogpt_platform/backend/backend/util/file.py +++ b/autogpt_platform/backend/backend/util/file.py @@ -12,6 +12,7 @@ from prisma.enums import WorkspaceFileSource from backend.util.cloud_storage import get_cloud_storage_handler from backend.util.request import Requests +from backend.util.settings import Config from backend.util.type import MediaFileType from backend.util.virus_scanner import scan_content_safe @@ -130,7 +131,7 @@ async def store_media_file( base_path.mkdir(parents=True, exist_ok=True) # Security fix: Add disk space limits to prevent DoS - MAX_FILE_SIZE = 100 * 1024 * 1024 # 100MB per file + MAX_FILE_SIZE_BYTES = Config().max_file_size_mb * 1024 * 1024 MAX_TOTAL_DISK_USAGE = 1024 * 1024 * 1024 # 1GB total per execution directory # Check total disk usage in base_path @@ -210,9 +211,9 @@ async def store_media_file( raise ValueError(f"Invalid file path '{filename}': {e}") from e # Check file size limit - if len(workspace_content) > MAX_FILE_SIZE: + if len(workspace_content) > MAX_FILE_SIZE_BYTES: raise ValueError( - f"File too large: {len(workspace_content)} bytes > {MAX_FILE_SIZE} bytes" + f"File too large: {len(workspace_content)} bytes > {MAX_FILE_SIZE_BYTES} bytes" ) # Virus scan the workspace content before writing locally @@ -235,9 +236,9 @@ async def store_media_file( raise ValueError(f"Invalid file path '{filename}': {e}") from e # Check file size limit - if len(cloud_content) > MAX_FILE_SIZE: + if len(cloud_content) > MAX_FILE_SIZE_BYTES: raise ValueError( - f"File too large: {len(cloud_content)} bytes > {MAX_FILE_SIZE} bytes" + f"File too large: {len(cloud_content)} bytes > {MAX_FILE_SIZE_BYTES} bytes" ) # Virus scan the cloud content before writing locally @@ -265,9 +266,9 @@ async def store_media_file( content = base64.b64decode(b64_content) # Check file size limit - if len(content) > MAX_FILE_SIZE: + if len(content) > MAX_FILE_SIZE_BYTES: raise ValueError( - f"File too large: {len(content)} bytes > {MAX_FILE_SIZE} bytes" + f"File too large: {len(content)} bytes > {MAX_FILE_SIZE_BYTES} bytes" ) # Virus scan the base64 content before writing @@ -287,9 +288,9 @@ async def store_media_file( resp = await Requests().get(file) # Check file size limit - if len(resp.content) > MAX_FILE_SIZE: + if len(resp.content) > MAX_FILE_SIZE_BYTES: raise ValueError( - f"File too large: {len(resp.content)} bytes > {MAX_FILE_SIZE} bytes" + f"File too large: {len(resp.content)} bytes > {MAX_FILE_SIZE_BYTES} bytes" ) # Virus scan the downloaded content before writing diff --git a/autogpt_platform/backend/backend/util/settings.py b/autogpt_platform/backend/backend/util/settings.py index d7c15a16d8..aa28a4c9ac 100644 --- a/autogpt_platform/backend/backend/util/settings.py +++ b/autogpt_platform/backend/backend/util/settings.py @@ -395,6 +395,13 @@ class Config(UpdateTrackingModel["Config"], BaseSettings): description="Maximum file size in MB for file uploads (1-1024 MB)", ) + max_file_size_mb: int = Field( + default=100, + ge=1, + le=1024, + description="Maximum file size in MB for workspace files (1-1024 MB)", + ) + # AutoMod configuration automod_enabled: bool = Field( default=False, diff --git a/autogpt_platform/backend/backend/util/workspace.py b/autogpt_platform/backend/backend/util/workspace.py index ce46a1e646..4cf81da779 100644 --- a/autogpt_platform/backend/backend/util/workspace.py +++ b/autogpt_platform/backend/backend/util/workspace.py @@ -22,13 +22,11 @@ from backend.data.workspace import ( list_workspace_files, soft_delete_workspace_file, ) +from backend.util.settings import Config from backend.util.workspace_storage import compute_file_checksum, get_workspace_storage logger = logging.getLogger(__name__) -# Maximum file size: 100MB per file -MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024 - class WorkspaceManager: """ @@ -160,10 +158,11 @@ class WorkspaceManager: ValueError: If file exceeds size limit or path already exists """ # Enforce file size limit - if len(content) > MAX_FILE_SIZE_BYTES: + max_file_size = Config().max_file_size_mb * 1024 * 1024 + if len(content) > max_file_size: raise ValueError( f"File too large: {len(content)} bytes exceeds " - f"{MAX_FILE_SIZE_BYTES // (1024*1024)}MB limit" + f"{Config().max_file_size_mb}MB limit" ) # Determine path with session scoping @@ -209,6 +208,7 @@ class WorkspaceManager: try: file = await create_workspace_file( workspace_id=self.workspace_id, + file_id=file_id, name=filename, path=path, storage_path=storage_path, @@ -229,6 +229,7 @@ class WorkspaceManager: # Retry the create file = await create_workspace_file( workspace_id=self.workspace_id, + file_id=file_id, name=filename, path=path, storage_path=storage_path,