diff --git a/autogpt_platform/autogpt_libs/autogpt_libs/logging/config.py b/autogpt_platform/autogpt_libs/autogpt_libs/logging/config.py index d424e6ba8c..93a4030bcc 100644 --- a/autogpt_platform/autogpt_libs/autogpt_libs/logging/config.py +++ b/autogpt_platform/autogpt_libs/autogpt_libs/logging/config.py @@ -4,6 +4,7 @@ import logging import os import socket import sys +from logging.handlers import RotatingFileHandler from pathlib import Path from pydantic import Field, field_validator @@ -139,8 +140,13 @@ def configure_logging(force_cloud_logging: bool = False) -> None: print(f"Log directory: {config.log_dir}") # Activity log handler (INFO and above) - activity_log_handler = logging.FileHandler( - config.log_dir / LOG_FILE, "a", "utf-8" + # Security fix: Use RotatingFileHandler with size limits to prevent disk exhaustion + activity_log_handler = RotatingFileHandler( + config.log_dir / LOG_FILE, + mode="a", + encoding="utf-8", + maxBytes=10 * 1024 * 1024, # 10MB per file + backupCount=3, # Keep 3 backup files (40MB total) ) activity_log_handler.setLevel(config.level) activity_log_handler.setFormatter( @@ -150,8 +156,13 @@ def configure_logging(force_cloud_logging: bool = False) -> None: if config.level == logging.DEBUG: # Debug log handler (all levels) - debug_log_handler = logging.FileHandler( - config.log_dir / DEBUG_LOG_FILE, "a", "utf-8" + # Security fix: Use RotatingFileHandler with size limits + debug_log_handler = RotatingFileHandler( + config.log_dir / DEBUG_LOG_FILE, + mode="a", + encoding="utf-8", + maxBytes=10 * 1024 * 1024, # 10MB per file + backupCount=3, # Keep 3 backup files (40MB total) ) debug_log_handler.setLevel(logging.DEBUG) debug_log_handler.setFormatter( @@ -160,8 +171,13 @@ def configure_logging(force_cloud_logging: bool = False) -> None: log_handlers.append(debug_log_handler) # Error log handler (ERROR and above) - error_log_handler = logging.FileHandler( - config.log_dir / ERROR_LOG_FILE, "a", "utf-8" + # Security fix: Use RotatingFileHandler with size limits + error_log_handler = RotatingFileHandler( + config.log_dir / ERROR_LOG_FILE, + mode="a", + encoding="utf-8", + maxBytes=10 * 1024 * 1024, # 10MB per file + backupCount=3, # Keep 3 backup files (40MB total) ) error_log_handler.setLevel(logging.ERROR) error_log_handler.setFormatter(AGPTFormatter(DEBUG_LOG_FORMAT, no_color=True)) diff --git a/autogpt_platform/backend/backend/blocks/code_extraction_block.py b/autogpt_platform/backend/backend/blocks/code_extraction_block.py index 33bf225bfd..c421d40092 100644 --- a/autogpt_platform/backend/backend/blocks/code_extraction_block.py +++ b/autogpt_platform/backend/backend/blocks/code_extraction_block.py @@ -90,7 +90,7 @@ class CodeExtractionBlock(Block): for aliases in language_aliases.values() for alias in aliases ) - + r")\s+[\s\S]*?```" + + r")[ \t]*\n[\s\S]*?```" ) remaining_text = re.sub(pattern, "", input_data.text).strip() @@ -103,7 +103,9 @@ class CodeExtractionBlock(Block): # Escape special regex characters in the language string language = re.escape(language) # Extract all code blocks enclosed in ```language``` blocks - pattern = re.compile(rf"```{language}\s+(.*?)```", re.DOTALL | re.IGNORECASE) + pattern = re.compile( + rf"```{language}[ \t]*\n(.*?)\n```", re.DOTALL | re.IGNORECASE + ) matches = pattern.finditer(text) # Combine all code blocks for this language with newlines between them code_blocks = [match.group(1).strip() for match in matches] diff --git a/autogpt_platform/backend/backend/blocks/iteration.py b/autogpt_platform/backend/backend/blocks/iteration.py index c0b66a2ed0..1f3753b901 100644 --- a/autogpt_platform/backend/backend/blocks/iteration.py +++ b/autogpt_platform/backend/backend/blocks/iteration.py @@ -54,20 +54,43 @@ class StepThroughItemsBlock(Block): ) async def run(self, input_data: Input, **kwargs) -> BlockOutput: + # Security fix: Add limits to prevent DoS from large iterations + MAX_ITEMS = 10000 # Maximum items to iterate + MAX_ITEM_SIZE = 1024 * 1024 # 1MB per item + for data in [input_data.items, input_data.items_object, input_data.items_str]: if not data: continue + + # Limit string size before parsing if isinstance(data, str): + if len(data) > MAX_ITEM_SIZE: + raise ValueError( + f"Input too large: {len(data)} bytes > {MAX_ITEM_SIZE} bytes" + ) items = json.loads(data) else: items = data + + # Check total item count + if isinstance(items, (list, dict)): + if len(items) > MAX_ITEMS: + raise ValueError(f"Too many items: {len(items)} > {MAX_ITEMS}") + + iteration_count = 0 if isinstance(items, dict): # If items is a dictionary, iterate over its values - for item in items.values(): - yield "item", item - yield "key", item + for key, value in items.items(): + if iteration_count >= MAX_ITEMS: + break + yield "item", value + yield "key", key # Fixed: should yield key, not item + iteration_count += 1 else: # If items is a list, iterate over the list for index, item in enumerate(items): + if iteration_count >= MAX_ITEMS: + break yield "item", item yield "key", index + iteration_count += 1 diff --git a/autogpt_platform/backend/backend/blocks/llm.py b/autogpt_platform/backend/backend/blocks/llm.py index facaa92c30..b3260e797f 100644 --- a/autogpt_platform/backend/backend/blocks/llm.py +++ b/autogpt_platform/backend/backend/blocks/llm.py @@ -1404,11 +1404,27 @@ class AITextSummarizerBlock(AIBlockBase): @staticmethod def _split_text(text: str, max_tokens: int, overlap: int) -> list[str]: + # Security fix: Add validation to prevent DoS attacks + # Limit text size to prevent memory exhaustion + MAX_TEXT_LENGTH = 1_000_000 # 1MB character limit + MAX_CHUNKS = 100 # Maximum number of chunks to prevent excessive memory use + + if len(text) > MAX_TEXT_LENGTH: + text = text[:MAX_TEXT_LENGTH] + + # Ensure chunk_size is at least 1 to prevent infinite loops + chunk_size = max(1, max_tokens - overlap) + + # Ensure overlap is less than max_tokens to prevent invalid configurations + if overlap >= max_tokens: + overlap = max(0, max_tokens - 1) + words = text.split() chunks = [] - chunk_size = max_tokens - overlap for i in range(0, len(words), chunk_size): + if len(chunks) >= MAX_CHUNKS: + break # Limit the number of chunks to prevent memory exhaustion chunk = " ".join(words[i : i + max_tokens]) chunks.append(chunk) diff --git a/autogpt_platform/backend/backend/blocks/rss.py b/autogpt_platform/backend/backend/blocks/rss.py index 751506fcb8..8d8dc91d09 100644 --- a/autogpt_platform/backend/backend/blocks/rss.py +++ b/autogpt_platform/backend/backend/blocks/rss.py @@ -1,4 +1,7 @@ import asyncio +import logging +import urllib.parse +import urllib.request from datetime import datetime, timedelta, timezone from typing import Any @@ -101,7 +104,38 @@ class ReadRSSFeedBlock(Block): @staticmethod def parse_feed(url: str) -> dict[str, Any]: - return feedparser.parse(url) # type: ignore + # Security fix: Add protection against memory exhaustion attacks + MAX_FEED_SIZE = 10 * 1024 * 1024 # 10MB limit for RSS feeds + + # Validate URL + parsed_url = urllib.parse.urlparse(url) + if parsed_url.scheme not in ("http", "https"): + raise ValueError(f"Invalid URL scheme: {parsed_url.scheme}") + + # Download with size limit + try: + with urllib.request.urlopen(url, timeout=30) as response: + # Check content length if available + content_length = response.headers.get("Content-Length") + if content_length and int(content_length) > MAX_FEED_SIZE: + raise ValueError( + f"Feed too large: {content_length} bytes exceeds {MAX_FEED_SIZE} limit" + ) + + # Read with size limit + content = response.read(MAX_FEED_SIZE + 1) + if len(content) > MAX_FEED_SIZE: + raise ValueError( + f"Feed too large: exceeds {MAX_FEED_SIZE} byte limit" + ) + + # Parse with feedparser using the validated content + # feedparser has built-in protection against XML attacks + return feedparser.parse(content) # type: ignore + except Exception as e: + # Log error and return empty feed + logging.warning(f"Failed to parse RSS feed from {url}: {e}") + return {"entries": []} async def run(self, input_data: Input, **kwargs) -> BlockOutput: keep_going = True diff --git a/autogpt_platform/backend/backend/blocks/test/test_blocks_dos_vulnerability.py b/autogpt_platform/backend/backend/blocks/test/test_blocks_dos_vulnerability.py new file mode 100644 index 0000000000..87bf102099 --- /dev/null +++ b/autogpt_platform/backend/backend/blocks/test/test_blocks_dos_vulnerability.py @@ -0,0 +1,269 @@ +""" +Test security fixes for various DoS vulnerabilities. +""" + +import asyncio +from unittest.mock import patch + +import pytest + +from backend.blocks.code_extraction_block import CodeExtractionBlock +from backend.blocks.iteration import StepThroughItemsBlock +from backend.blocks.llm import AITextSummarizerBlock +from backend.blocks.text import ExtractTextInformationBlock +from backend.blocks.xml_parser import XMLParserBlock +from backend.util.file import store_media_file +from backend.util.type import MediaFileType + + +class TestCodeExtractionBlockSecurity: + """Test ReDoS fixes in CodeExtractionBlock.""" + + async def test_redos_protection(self): + """Test that the regex patterns don't cause ReDoS.""" + block = CodeExtractionBlock() + + # Test with input that would previously cause ReDoS + malicious_input = "```python" + " " * 10000 # Large spaces + + result = [] + async for output_name, output_data in block.run( + CodeExtractionBlock.Input(text=malicious_input) + ): + result.append((output_name, output_data)) + + # Should complete without hanging + assert len(result) >= 1 + assert any(name == "remaining_text" for name, _ in result) + + +class TestAITextSummarizerBlockSecurity: + """Test memory exhaustion fixes in AITextSummarizerBlock.""" + + def test_split_text_limits(self): + """Test that _split_text has proper limits.""" + # Test text size limit + large_text = "a" * 2_000_000 # 2MB text + result = AITextSummarizerBlock._split_text(large_text, 1000, 100) + + # Should be truncated to 1MB + total_chars = sum(len(chunk) for chunk in result) + assert total_chars <= 1_000_000 + 1000 # Allow for chunk boundary + + # Test chunk count limit + result = AITextSummarizerBlock._split_text("word " * 10000, 10, 9) + assert len(result) <= 100 # MAX_CHUNKS limit + + # Test parameter validation + result = AITextSummarizerBlock._split_text( + "test", 10, 15 + ) # overlap > max_tokens + assert len(result) >= 1 # Should still work + + +class TestExtractTextInformationBlockSecurity: + """Test ReDoS and memory exhaustion fixes in ExtractTextInformationBlock.""" + + async def test_text_size_limits(self): + """Test text size limits.""" + block = ExtractTextInformationBlock() + + # Test with large input + large_text = "a" * 2_000_000 # 2MB + + results = [] + async for output_name, output_data in block.run( + ExtractTextInformationBlock.Input( + text=large_text, pattern=r"a+", find_all=True, group=0 + ) + ): + results.append((output_name, output_data)) + + # Should complete and have limits applied + matched_results = [r for name, r in results if name == "matched_results"] + if matched_results: + assert len(matched_results[0]) <= 1000 # MAX_MATCHES limit + + async def test_dangerous_pattern_timeout(self): + """Test timeout protection for dangerous patterns.""" + block = ExtractTextInformationBlock() + + # Test with potentially dangerous lookahead pattern + test_input = "a" * 1000 + + # This should complete quickly due to timeout protection + start_time = asyncio.get_event_loop().time() + results = [] + async for output_name, output_data in block.run( + ExtractTextInformationBlock.Input( + text=test_input, pattern=r"(?=.+)", find_all=True, group=0 + ) + ): + results.append((output_name, output_data)) + + end_time = asyncio.get_event_loop().time() + # Should complete within reasonable time (much less than 5s timeout) + assert (end_time - start_time) < 10 + + async def test_redos_catastrophic_backtracking(self): + """Test that ReDoS patterns with catastrophic backtracking are handled.""" + block = ExtractTextInformationBlock() + + # Pattern that causes catastrophic backtracking: (a+)+b + # With input "aaaaaaaaaaaaaaaaaaaaaaaaaaaa" (no 'b'), this causes exponential time + dangerous_pattern = r"(a+)+b" + test_input = "a" * 30 # 30 'a's without a 'b' at the end + + # This should be handled by timeout protection or pattern detection + start_time = asyncio.get_event_loop().time() + results = [] + + async for output_name, output_data in block.run( + ExtractTextInformationBlock.Input( + text=test_input, pattern=dangerous_pattern, find_all=True, group=0 + ) + ): + results.append((output_name, output_data)) + + end_time = asyncio.get_event_loop().time() + elapsed = end_time - start_time + + # Should complete within timeout (6 seconds to be safe) + # The current threading.Timer approach doesn't work, so this will likely fail + # demonstrating the need for a fix + assert elapsed < 6, f"Regex took {elapsed}s, timeout mechanism failed" + + # Should return empty results on timeout or no match + matched_results = [r for name, r in results if name == "matched_results"] + assert matched_results[0] == [] # No matches expected + + +class TestStepThroughItemsBlockSecurity: + """Test iteration limits in StepThroughItemsBlock.""" + + async def test_item_count_limits(self): + """Test maximum item count limits.""" + block = StepThroughItemsBlock() + + # Test with too many items + large_list = list(range(20000)) # Exceeds MAX_ITEMS (10000) + + with pytest.raises(ValueError, match="Too many items"): + async for _ in block.run(StepThroughItemsBlock.Input(items=large_list)): + pass + + async def test_string_size_limits(self): + """Test string input size limits.""" + block = StepThroughItemsBlock() + + # Test with large JSON string + large_string = '["item"]' * 200000 # Large JSON string + + with pytest.raises(ValueError, match="Input too large"): + async for _ in block.run( + StepThroughItemsBlock.Input(items_str=large_string) + ): + pass + + async def test_normal_iteration_works(self): + """Test that normal iteration still works.""" + block = StepThroughItemsBlock() + + results = [] + async for output_name, output_data in block.run( + StepThroughItemsBlock.Input(items=[1, 2, 3]) + ): + results.append((output_name, output_data)) + + # Should have 6 outputs (item, key for each of 3 items) + assert len(results) == 6 + items = [data for name, data in results if name == "item"] + assert items == [1, 2, 3] + + +class TestXMLParserBlockSecurity: + """Test XML size limits in XMLParserBlock.""" + + async def test_xml_size_limits(self): + """Test XML input size limits.""" + block = XMLParserBlock() + + # Test with large XML - need to exceed 10MB limit + # Each "data" is 17 chars, need ~620K items for >10MB + large_xml = "" + "data" * 620000 + "" + + with pytest.raises(ValueError, match="XML too large"): + async for _ in block.run(XMLParserBlock.Input(input_xml=large_xml)): + pass + + +class TestStoreMediaFileSecurity: + """Test file storage security limits.""" + + @patch("backend.util.file.scan_content_safe") + @patch("backend.util.file.get_cloud_storage_handler") + async def test_file_size_limits(self, mock_cloud_storage, mock_scan): + """Test file size limits.""" + # Mock cloud storage handler - get_cloud_storage_handler is async + # but is_cloud_path and parse_cloud_path are sync methods + from unittest.mock import MagicMock + + mock_handler = MagicMock() + mock_handler.is_cloud_path.return_value = False + + # Make get_cloud_storage_handler an async function that returns the mock handler + async def async_get_handler(): + return mock_handler + + mock_cloud_storage.side_effect = async_get_handler + mock_scan.return_value = None + + # Test with large base64 content + large_content = "a" * (200 * 1024 * 1024) # 200MB + large_data_uri = f"data:text/plain;base64,{large_content}" + + with pytest.raises(ValueError, match="File too large"): + await store_media_file( + graph_exec_id="test", + file=MediaFileType(large_data_uri), + user_id="test_user", + ) + + @patch("backend.util.file.Path") + @patch("backend.util.file.scan_content_safe") + @patch("backend.util.file.get_cloud_storage_handler") + async def test_directory_size_limits(self, mock_cloud_storage, mock_scan, MockPath): + """Test directory size limits.""" + from unittest.mock import MagicMock + + mock_handler = MagicMock() + mock_handler.is_cloud_path.return_value = False + + async def async_get_handler(): + return mock_handler + + mock_cloud_storage.side_effect = async_get_handler + mock_scan.return_value = None + + # Create mock path instance for the execution directory + mock_path_instance = MagicMock() + mock_path_instance.exists.return_value = True + + # Mock glob to return files that total > 1GB + mock_file = MagicMock() + mock_file.is_file.return_value = True + mock_file.stat.return_value.st_size = 2 * 1024 * 1024 * 1024 # 2GB + mock_path_instance.glob.return_value = [mock_file] + + # Make Path() return our mock + MockPath.return_value = mock_path_instance + + # Should raise an error when directory size exceeds limit + with pytest.raises(ValueError, match="Disk usage limit exceeded"): + await store_media_file( + graph_exec_id="test", + file=MediaFileType( + "data:text/plain;base64,dGVzdA==" + ), # Small test file + user_id="test_user", + ) diff --git a/autogpt_platform/backend/backend/blocks/text.py b/autogpt_platform/backend/backend/blocks/text.py index 2ebb491074..b6dae2c840 100644 --- a/autogpt_platform/backend/backend/blocks/text.py +++ b/autogpt_platform/backend/backend/blocks/text.py @@ -2,6 +2,8 @@ import re from pathlib import Path from typing import Any +import regex # Has built-in timeout support + from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema from backend.data.model import SchemaField from backend.util import json, text @@ -137,6 +139,11 @@ class ExtractTextInformationBlock(Block): ) async def run(self, input_data: Input, **kwargs) -> BlockOutput: + # Security fix: Add limits to prevent ReDoS and memory exhaustion + MAX_TEXT_LENGTH = 1_000_000 # 1MB character limit + MAX_MATCHES = 1000 # Maximum number of matches to prevent memory exhaustion + MAX_MATCH_LENGTH = 10_000 # Maximum length per match + flags = 0 if not input_data.case_sensitive: flags = flags | re.IGNORECASE @@ -148,20 +155,85 @@ class ExtractTextInformationBlock(Block): else: txt = json.dumps(input_data.text) - matches = [ - match.group(input_data.group) - for match in re.finditer(input_data.pattern, txt, flags) - if input_data.group <= len(match.groups()) - ] - if not input_data.find_all: - matches = matches[:1] - for match in matches: - yield "positive", match - if not matches: - yield "negative", input_data.text + # Limit text size to prevent DoS + if len(txt) > MAX_TEXT_LENGTH: + txt = txt[:MAX_TEXT_LENGTH] - yield "matched_results", matches - yield "matched_count", len(matches) + # Validate regex pattern to prevent dangerous patterns + dangerous_patterns = [ + r".*\+.*\+", # Nested quantifiers + r".*\*.*\*", # Nested quantifiers + r"(?=.*\+)", # Lookahead with quantifier + r"(?=.*\*)", # Lookahead with quantifier + r"\(.+\)\+", # Group with nested quantifier + r"\(.+\)\*", # Group with nested quantifier + r"\([^)]+\+\)\+", # Nested quantifiers like (a+)+ + r"\([^)]+\*\)\*", # Nested quantifiers like (a*)* + ] + + # Check if pattern is potentially dangerous + is_dangerous = any( + re.search(dangerous, input_data.pattern) for dangerous in dangerous_patterns + ) + + # Use regex module with timeout for dangerous patterns + # For safe patterns, use standard re module for compatibility + try: + matches = [] + match_count = 0 + + if is_dangerous: + # Use regex module with timeout (5 seconds) for dangerous patterns + # The regex module supports timeout parameter in finditer + try: + for match in regex.finditer( + input_data.pattern, txt, flags=flags, timeout=5.0 + ): + if match_count >= MAX_MATCHES: + break + if input_data.group <= len(match.groups()): + match_text = match.group(input_data.group) + # Limit match length to prevent memory exhaustion + if len(match_text) > MAX_MATCH_LENGTH: + match_text = match_text[:MAX_MATCH_LENGTH] + matches.append(match_text) + match_count += 1 + except regex.error as e: + # Timeout occurred or regex error + if "timeout" in str(e).lower(): + # Timeout - return empty results + pass + else: + # Other regex error + raise + else: + # Use standard re module for non-dangerous patterns + for match in re.finditer(input_data.pattern, txt, flags): + if match_count >= MAX_MATCHES: + break + if input_data.group <= len(match.groups()): + match_text = match.group(input_data.group) + # Limit match length to prevent memory exhaustion + if len(match_text) > MAX_MATCH_LENGTH: + match_text = match_text[:MAX_MATCH_LENGTH] + matches.append(match_text) + match_count += 1 + + if not input_data.find_all: + matches = matches[:1] + + for match in matches: + yield "positive", match + if not matches: + yield "negative", input_data.text + + yield "matched_results", matches + yield "matched_count", len(matches) + except Exception: + # Return empty results on any regex error + yield "negative", input_data.text + yield "matched_results", [] + yield "matched_count", 0 class FillTextTemplateBlock(Block): diff --git a/autogpt_platform/backend/backend/blocks/xml_parser.py b/autogpt_platform/backend/backend/blocks/xml_parser.py index a3d5854499..9ff4578a06 100644 --- a/autogpt_platform/backend/backend/blocks/xml_parser.py +++ b/autogpt_platform/backend/backend/blocks/xml_parser.py @@ -26,6 +26,14 @@ class XMLParserBlock(Block): ) async def run(self, input_data: Input, **kwargs) -> BlockOutput: + # Security fix: Add size limits to prevent XML bomb attacks + MAX_XML_SIZE = 10 * 1024 * 1024 # 10MB limit for XML input + + if len(input_data.input_xml) > MAX_XML_SIZE: + raise ValueError( + f"XML too large: {len(input_data.input_xml)} bytes > {MAX_XML_SIZE} bytes" + ) + try: tokens = tokenize(input_data.input_xml) parser = Parser(tokens) diff --git a/autogpt_platform/backend/backend/executor/activity_status_generator.py b/autogpt_platform/backend/backend/executor/activity_status_generator.py index b0aa1c1243..b9d1ed7bd5 100644 --- a/autogpt_platform/backend/backend/executor/activity_status_generator.py +++ b/autogpt_platform/backend/backend/executor/activity_status_generator.py @@ -4,7 +4,12 @@ Module for generating AI-based activity status for graph executions. import json import logging -from typing import TYPE_CHECKING, Any, NotRequired, TypedDict +from typing import TYPE_CHECKING, Any, TypedDict + +try: + from typing import NotRequired +except ImportError: + from typing_extensions import NotRequired from pydantic import SecretStr diff --git a/autogpt_platform/backend/backend/util/file.py b/autogpt_platform/backend/backend/util/file.py index 1fc52f9e6e..edf302fb34 100644 --- a/autogpt_platform/backend/backend/util/file.py +++ b/autogpt_platform/backend/backend/util/file.py @@ -66,6 +66,18 @@ async def store_media_file( base_path = Path(get_exec_file_path(graph_exec_id, "")) 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_TOTAL_DISK_USAGE = 1024 * 1024 * 1024 # 1GB total per execution directory + + # Check total disk usage in base_path + if base_path.exists(): + current_usage = get_dir_size(base_path) + if current_usage > MAX_TOTAL_DISK_USAGE: + raise ValueError( + f"Disk usage limit exceeded: {current_usage} bytes > {MAX_TOTAL_DISK_USAGE} bytes" + ) + # Helper functions def _extension_from_mime(mime: str) -> str: ext = mimetypes.guess_extension(mime, strict=False) @@ -108,6 +120,12 @@ async def store_media_file( filename = Path(path_part).name or f"{uuid.uuid4()}.bin" target_path = _ensure_inside_base(base_path / filename, base_path) + # Check file size limit + if len(cloud_content) > MAX_FILE_SIZE: + raise ValueError( + f"File too large: {len(cloud_content)} bytes > {MAX_FILE_SIZE} bytes" + ) + # Virus scan the cloud content before writing locally await scan_content_safe(cloud_content, filename=filename) target_path.write_bytes(cloud_content) @@ -129,6 +147,12 @@ async def store_media_file( target_path = _ensure_inside_base(base_path / filename, base_path) content = base64.b64decode(b64_content) + # Check file size limit + if len(content) > MAX_FILE_SIZE: + raise ValueError( + f"File too large: {len(content)} bytes > {MAX_FILE_SIZE} bytes" + ) + # Virus scan the base64 content before writing await scan_content_safe(content, filename=filename) target_path.write_bytes(content) @@ -142,6 +166,12 @@ async def store_media_file( # Download and save resp = await Requests().get(file) + # Check file size limit + if len(resp.content) > MAX_FILE_SIZE: + raise ValueError( + f"File too large: {len(resp.content)} bytes > {MAX_FILE_SIZE} bytes" + ) + # Virus scan the downloaded content before writing await scan_content_safe(resp.content, filename=filename) target_path.write_bytes(resp.content) @@ -159,6 +189,18 @@ async def store_media_file( return MediaFileType(_strip_base_prefix(target_path, base_path)) +def get_dir_size(path: Path) -> int: + """Get total size of directory.""" + total = 0 + try: + for entry in path.glob("**/*"): + if entry.is_file(): + total += entry.stat().st_size + except Exception: + pass + return total + + def get_mime_type(file: str) -> str: """ Get the MIME type of a file, whether it's a data URI, URL, or local path. diff --git a/autogpt_platform/backend/poetry.lock b/autogpt_platform/backend/poetry.lock index 341f4379fa..ce769d4272 100644 --- a/autogpt_platform/backend/poetry.lock +++ b/autogpt_platform/backend/poetry.lock @@ -5377,106 +5377,127 @@ typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""} [[package]] name = "regex" -version = "2024.11.6" +version = "2025.9.18" description = "Alternative regular expression module, to replace re." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, - {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, - {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, - {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, - {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, - {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, - {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, - {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, - {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, - {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, - {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, - {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, - {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, - {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, - {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, - {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, + {file = "regex-2025.9.18-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:12296202480c201c98a84aecc4d210592b2f55e200a1d193235c4db92b9f6788"}, + {file = "regex-2025.9.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:220381f1464a581f2ea988f2220cf2a67927adcef107d47d6897ba5a2f6d51a4"}, + {file = "regex-2025.9.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:87f681bfca84ebd265278b5daa1dcb57f4db315da3b5d044add7c30c10442e61"}, + {file = "regex-2025.9.18-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34d674cbba70c9398074c8a1fcc1a79739d65d1105de2a3c695e2b05ea728251"}, + {file = "regex-2025.9.18-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:385c9b769655cb65ea40b6eea6ff763cbb6d69b3ffef0b0db8208e1833d4e746"}, + {file = "regex-2025.9.18-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8900b3208e022570ae34328712bef6696de0804c122933414014bae791437ab2"}, + {file = "regex-2025.9.18-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c204e93bf32cd7a77151d44b05eb36f469d0898e3fba141c026a26b79d9914a0"}, + {file = "regex-2025.9.18-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3acc471d1dd7e5ff82e6cacb3b286750decd949ecd4ae258696d04f019817ef8"}, + {file = "regex-2025.9.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6479d5555122433728760e5f29edb4c2b79655a8deb681a141beb5c8a025baea"}, + {file = "regex-2025.9.18-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:431bd2a8726b000eb6f12429c9b438a24062a535d06783a93d2bcbad3698f8a8"}, + {file = "regex-2025.9.18-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0cc3521060162d02bd36927e20690129200e5ac9d2c6d32b70368870b122db25"}, + {file = "regex-2025.9.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a021217b01be2d51632ce056d7a837d3fa37c543ede36e39d14063176a26ae29"}, + {file = "regex-2025.9.18-cp310-cp310-win32.whl", hash = "sha256:4a12a06c268a629cb67cc1d009b7bb0be43e289d00d5111f86a2efd3b1949444"}, + {file = "regex-2025.9.18-cp310-cp310-win_amd64.whl", hash = "sha256:47acd811589301298c49db2c56bde4f9308d6396da92daf99cba781fa74aa450"}, + {file = "regex-2025.9.18-cp310-cp310-win_arm64.whl", hash = "sha256:16bd2944e77522275e5ee36f867e19995bcaa533dcb516753a26726ac7285442"}, + {file = "regex-2025.9.18-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:51076980cd08cd13c88eb7365427ae27f0d94e7cebe9ceb2bb9ffdae8fc4d82a"}, + {file = "regex-2025.9.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:828446870bd7dee4e0cbeed767f07961aa07f0ea3129f38b3ccecebc9742e0b8"}, + {file = "regex-2025.9.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c28821d5637866479ec4cc23b8c990f5bc6dd24e5e4384ba4a11d38a526e1414"}, + {file = "regex-2025.9.18-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:726177ade8e481db669e76bf99de0b278783be8acd11cef71165327abd1f170a"}, + {file = "regex-2025.9.18-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5cca697da89b9f8ea44115ce3130f6c54c22f541943ac8e9900461edc2b8bd4"}, + {file = "regex-2025.9.18-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dfbde38f38004703c35666a1e1c088b778e35d55348da2b7b278914491698d6a"}, + {file = "regex-2025.9.18-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f2f422214a03fab16bfa495cfec72bee4aaa5731843b771860a471282f1bf74f"}, + {file = "regex-2025.9.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a295916890f4df0902e4286bc7223ee7f9e925daa6dcdec4192364255b70561a"}, + {file = "regex-2025.9.18-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:5db95ff632dbabc8c38c4e82bf545ab78d902e81160e6e455598014f0abe66b9"}, + {file = "regex-2025.9.18-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fb967eb441b0f15ae610b7069bdb760b929f267efbf522e814bbbfffdf125ce2"}, + {file = "regex-2025.9.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f04d2f20da4053d96c08f7fde6e1419b7ec9dbcee89c96e3d731fca77f411b95"}, + {file = "regex-2025.9.18-cp311-cp311-win32.whl", hash = "sha256:895197241fccf18c0cea7550c80e75f185b8bd55b6924fcae269a1a92c614a07"}, + {file = "regex-2025.9.18-cp311-cp311-win_amd64.whl", hash = "sha256:7e2b414deae99166e22c005e154a5513ac31493db178d8aec92b3269c9cce8c9"}, + {file = "regex-2025.9.18-cp311-cp311-win_arm64.whl", hash = "sha256:fb137ec7c5c54f34a25ff9b31f6b7b0c2757be80176435bf367111e3f71d72df"}, + {file = "regex-2025.9.18-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:436e1b31d7efd4dcd52091d076482031c611dde58bf9c46ca6d0a26e33053a7e"}, + {file = "regex-2025.9.18-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c190af81e5576b9c5fdc708f781a52ff20f8b96386c6e2e0557a78402b029f4a"}, + {file = "regex-2025.9.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e4121f1ce2b2b5eec4b397cc1b277686e577e658d8f5870b7eb2d726bd2300ab"}, + {file = "regex-2025.9.18-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:300e25dbbf8299d87205e821a201057f2ef9aa3deb29caa01cd2cac669e508d5"}, + {file = "regex-2025.9.18-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7b47fcf9f5316c0bdaf449e879407e1b9937a23c3b369135ca94ebc8d74b1742"}, + {file = "regex-2025.9.18-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:57a161bd3acaa4b513220b49949b07e252165e6b6dc910ee7617a37ff4f5b425"}, + {file = "regex-2025.9.18-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f130c3a7845ba42de42f380fff3c8aebe89a810747d91bcf56d40a069f15352"}, + {file = "regex-2025.9.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f96fa342b6f54dcba928dd452e8d8cb9f0d63e711d1721cd765bb9f73bb048d"}, + {file = "regex-2025.9.18-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0f0d676522d68c207828dcd01fb6f214f63f238c283d9f01d85fc664c7c85b56"}, + {file = "regex-2025.9.18-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:40532bff8a1a0621e7903ae57fce88feb2e8a9a9116d341701302c9302aef06e"}, + {file = "regex-2025.9.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:039f11b618ce8d71a1c364fdee37da1012f5a3e79b1b2819a9f389cd82fd6282"}, + {file = "regex-2025.9.18-cp312-cp312-win32.whl", hash = "sha256:e1dd06f981eb226edf87c55d523131ade7285137fbde837c34dc9d1bf309f459"}, + {file = "regex-2025.9.18-cp312-cp312-win_amd64.whl", hash = "sha256:3d86b5247bf25fa3715e385aa9ff272c307e0636ce0c9595f64568b41f0a9c77"}, + {file = "regex-2025.9.18-cp312-cp312-win_arm64.whl", hash = "sha256:032720248cbeeae6444c269b78cb15664458b7bb9ed02401d3da59fe4d68c3a5"}, + {file = "regex-2025.9.18-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2a40f929cd907c7e8ac7566ac76225a77701a6221bca937bdb70d56cb61f57b2"}, + {file = "regex-2025.9.18-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c90471671c2cdf914e58b6af62420ea9ecd06d1554d7474d50133ff26ae88feb"}, + {file = "regex-2025.9.18-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a351aff9e07a2dabb5022ead6380cff17a4f10e4feb15f9100ee56c4d6d06af"}, + {file = "regex-2025.9.18-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc4b8e9d16e20ddfe16430c23468a8707ccad3365b06d4536142e71823f3ca29"}, + {file = "regex-2025.9.18-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b8cdbddf2db1c5e80338ba2daa3cfa3dec73a46fff2a7dda087c8efbf12d62f"}, + {file = "regex-2025.9.18-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a276937d9d75085b2c91fb48244349c6954f05ee97bba0963ce24a9d915b8b68"}, + {file = "regex-2025.9.18-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92a8e375ccdc1256401c90e9dc02b8642894443d549ff5e25e36d7cf8a80c783"}, + {file = "regex-2025.9.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0dc6893b1f502d73037cf807a321cdc9be29ef3d6219f7970f842475873712ac"}, + {file = "regex-2025.9.18-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a61e85bfc63d232ac14b015af1261f826260c8deb19401c0597dbb87a864361e"}, + {file = "regex-2025.9.18-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1ef86a9ebc53f379d921fb9a7e42b92059ad3ee800fcd9e0fe6181090e9f6c23"}, + {file = "regex-2025.9.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d3bc882119764ba3a119fbf2bd4f1b47bc56c1da5d42df4ed54ae1e8e66fdf8f"}, + {file = "regex-2025.9.18-cp313-cp313-win32.whl", hash = "sha256:3810a65675845c3bdfa58c3c7d88624356dd6ee2fc186628295e0969005f928d"}, + {file = "regex-2025.9.18-cp313-cp313-win_amd64.whl", hash = "sha256:16eaf74b3c4180ede88f620f299e474913ab6924d5c4b89b3833bc2345d83b3d"}, + {file = "regex-2025.9.18-cp313-cp313-win_arm64.whl", hash = "sha256:4dc98ba7dd66bd1261927a9f49bd5ee2bcb3660f7962f1ec02617280fc00f5eb"}, + {file = "regex-2025.9.18-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:fe5d50572bc885a0a799410a717c42b1a6b50e2f45872e2b40f4f288f9bce8a2"}, + {file = "regex-2025.9.18-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b9d9a2d6cda6621551ca8cf7a06f103adf72831153f3c0d982386110870c4d3"}, + {file = "regex-2025.9.18-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:13202e4c4ac0ef9a317fff817674b293c8f7e8c68d3190377d8d8b749f566e12"}, + {file = "regex-2025.9.18-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:874ff523b0fecffb090f80ae53dc93538f8db954c8bb5505f05b7787ab3402a0"}, + {file = "regex-2025.9.18-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d13ab0490128f2bb45d596f754148cd750411afc97e813e4b3a61cf278a23bb6"}, + {file = "regex-2025.9.18-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:05440bc172bc4b4b37fb9667e796597419404dbba62e171e1f826d7d2a9ebcef"}, + {file = "regex-2025.9.18-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5514b8e4031fdfaa3d27e92c75719cbe7f379e28cacd939807289bce76d0e35a"}, + {file = "regex-2025.9.18-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:65d3c38c39efce73e0d9dc019697b39903ba25b1ad45ebbd730d2cf32741f40d"}, + {file = "regex-2025.9.18-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ae77e447ebc144d5a26d50055c6ddba1d6ad4a865a560ec7200b8b06bc529368"}, + {file = "regex-2025.9.18-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e3ef8cf53dc8df49d7e28a356cf824e3623764e9833348b655cfed4524ab8a90"}, + {file = "regex-2025.9.18-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9feb29817df349c976da9a0debf775c5c33fc1c8ad7b9f025825da99374770b7"}, + {file = "regex-2025.9.18-cp313-cp313t-win32.whl", hash = "sha256:168be0d2f9b9d13076940b1ed774f98595b4e3c7fc54584bba81b3cc4181742e"}, + {file = "regex-2025.9.18-cp313-cp313t-win_amd64.whl", hash = "sha256:d59ecf3bb549e491c8104fea7313f3563c7b048e01287db0a90485734a70a730"}, + {file = "regex-2025.9.18-cp313-cp313t-win_arm64.whl", hash = "sha256:dbef80defe9fb21310948a2595420b36c6d641d9bea4c991175829b2cc4bc06a"}, + {file = "regex-2025.9.18-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c6db75b51acf277997f3adcd0ad89045d856190d13359f15ab5dda21581d9129"}, + {file = "regex-2025.9.18-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8f9698b6f6895d6db810e0bda5364f9ceb9e5b11328700a90cae573574f61eea"}, + {file = "regex-2025.9.18-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29cd86aa7cb13a37d0f0d7c21d8d949fe402ffa0ea697e635afedd97ab4b69f1"}, + {file = "regex-2025.9.18-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c9f285a071ee55cd9583ba24dde006e53e17780bb309baa8e4289cd472bcc47"}, + {file = "regex-2025.9.18-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5adf266f730431e3be9021d3e5b8d5ee65e563fec2883ea8093944d21863b379"}, + {file = "regex-2025.9.18-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1137cabc0f38807de79e28d3f6e3e3f2cc8cfb26bead754d02e6d1de5f679203"}, + {file = "regex-2025.9.18-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cc9e5525cada99699ca9223cce2d52e88c52a3d2a0e842bd53de5497c604164"}, + {file = "regex-2025.9.18-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bbb9246568f72dce29bcd433517c2be22c7791784b223a810225af3b50d1aafb"}, + {file = "regex-2025.9.18-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6a52219a93dd3d92c675383efff6ae18c982e2d7651c792b1e6d121055808743"}, + {file = "regex-2025.9.18-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:ae9b3840c5bd456780e3ddf2f737ab55a79b790f6409182012718a35c6d43282"}, + {file = "regex-2025.9.18-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d488c236ac497c46a5ac2005a952c1a0e22a07be9f10c3e735bc7d1209a34773"}, + {file = "regex-2025.9.18-cp314-cp314-win32.whl", hash = "sha256:0c3506682ea19beefe627a38872d8da65cc01ffa25ed3f2e422dffa1474f0788"}, + {file = "regex-2025.9.18-cp314-cp314-win_amd64.whl", hash = "sha256:57929d0f92bebb2d1a83af372cd0ffba2263f13f376e19b1e4fa32aec4efddc3"}, + {file = "regex-2025.9.18-cp314-cp314-win_arm64.whl", hash = "sha256:6a4b44df31d34fa51aa5c995d3aa3c999cec4d69b9bd414a8be51984d859f06d"}, + {file = "regex-2025.9.18-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:b176326bcd544b5e9b17d6943f807697c0cb7351f6cfb45bf5637c95ff7e6306"}, + {file = "regex-2025.9.18-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0ffd9e230b826b15b369391bec167baed57c7ce39efc35835448618860995946"}, + {file = "regex-2025.9.18-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec46332c41add73f2b57e2f5b642f991f6b15e50e9f86285e08ffe3a512ac39f"}, + {file = "regex-2025.9.18-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b80fa342ed1ea095168a3f116637bd1030d39c9ff38dc04e54ef7c521e01fc95"}, + {file = "regex-2025.9.18-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4d97071c0ba40f0cf2a93ed76e660654c399a0a04ab7d85472239460f3da84b"}, + {file = "regex-2025.9.18-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0ac936537ad87cef9e0e66c5144484206c1354224ee811ab1519a32373e411f3"}, + {file = "regex-2025.9.18-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dec57f96d4def58c422d212d414efe28218d58537b5445cf0c33afb1b4768571"}, + {file = "regex-2025.9.18-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:48317233294648bf7cd068857f248e3a57222259a5304d32c7552e2284a1b2ad"}, + {file = "regex-2025.9.18-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:274687e62ea3cf54846a9b25fc48a04459de50af30a7bd0b61a9e38015983494"}, + {file = "regex-2025.9.18-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a78722c86a3e7e6aadf9579e3b0ad78d955f2d1f1a8ca4f67d7ca258e8719d4b"}, + {file = "regex-2025.9.18-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:06104cd203cdef3ade989a1c45b6215bf42f8b9dd705ecc220c173233f7cba41"}, + {file = "regex-2025.9.18-cp314-cp314t-win32.whl", hash = "sha256:2e1eddc06eeaffd249c0adb6fafc19e2118e6308c60df9db27919e96b5656096"}, + {file = "regex-2025.9.18-cp314-cp314t-win_amd64.whl", hash = "sha256:8620d247fb8c0683ade51217b459cb4a1081c0405a3072235ba43a40d355c09a"}, + {file = "regex-2025.9.18-cp314-cp314t-win_arm64.whl", hash = "sha256:b7531a8ef61de2c647cdf68b3229b071e46ec326b3138b2180acb4275f470b01"}, + {file = "regex-2025.9.18-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3dbcfcaa18e9480669030d07371713c10b4f1a41f791ffa5cb1a99f24e777f40"}, + {file = "regex-2025.9.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1e85f73ef7095f0380208269055ae20524bfde3f27c5384126ddccf20382a638"}, + {file = "regex-2025.9.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9098e29b3ea4ffffeade423f6779665e2a4f8db64e699c0ed737ef0db6ba7b12"}, + {file = "regex-2025.9.18-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90b6b7a2d0f45b7ecaaee1aec6b362184d6596ba2092dd583ffba1b78dd0231c"}, + {file = "regex-2025.9.18-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c81b892af4a38286101502eae7aec69f7cd749a893d9987a92776954f3943408"}, + {file = "regex-2025.9.18-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3b524d010973f2e1929aeb635418d468d869a5f77b52084d9f74c272189c251d"}, + {file = "regex-2025.9.18-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b498437c026a3d5d0be0020023ff76d70ae4d77118e92f6f26c9d0423452446"}, + {file = "regex-2025.9.18-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0716e4d6e58853d83f6563f3cf25c281ff46cf7107e5f11879e32cb0b59797d9"}, + {file = "regex-2025.9.18-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:065b6956749379d41db2625f880b637d4acc14c0a4de0d25d609a62850e96d36"}, + {file = "regex-2025.9.18-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d4a691494439287c08ddb9b5793da605ee80299dd31e95fa3f323fac3c33d9d4"}, + {file = "regex-2025.9.18-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ef8d10cc0989565bcbe45fb4439f044594d5c2b8919d3d229ea2c4238f1d55b0"}, + {file = "regex-2025.9.18-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4baeb1b16735ac969a7eeecc216f1f8b7caf60431f38a2671ae601f716a32d25"}, + {file = "regex-2025.9.18-cp39-cp39-win32.whl", hash = "sha256:8e5f41ad24a1e0b5dfcf4c4e5d9f5bd54c895feb5708dd0c1d0d35693b24d478"}, + {file = "regex-2025.9.18-cp39-cp39-win_amd64.whl", hash = "sha256:50e8290707f2fb8e314ab3831e594da71e062f1d623b05266f8cfe4db4949afd"}, + {file = "regex-2025.9.18-cp39-cp39-win_arm64.whl", hash = "sha256:039a9d7195fd88c943d7c777d4941e8ef736731947becce773c31a1009cb3c35"}, + {file = "regex-2025.9.18.tar.gz", hash = "sha256:c5ba23274c61c6fef447ba6a39333297d0c247f53059dba0bca415cac511edc4"}, ] [[package]] @@ -7252,4 +7273,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.14" -content-hash = "b2363edeebb91f410039c8d4b563f683c1edb0cf4bda4f3e6c287040e93639bc" +content-hash = "a19fcce9d4ab88f14eb1e5baa83e1e5a90c9995b04b84dae7cfa6257cf19012c" diff --git a/autogpt_platform/backend/pyproject.toml b/autogpt_platform/backend/pyproject.toml index 5af2114718..a9ad38a639 100644 --- a/autogpt_platform/backend/pyproject.toml +++ b/autogpt_platform/backend/pyproject.toml @@ -56,6 +56,7 @@ pytest-asyncio = "^1.1.0" python-dotenv = "^1.1.1" python-multipart = "^0.0.20" redis = "^6.2.0" +regex = "^2025.9.18" replicate = "^1.0.6" sentry-sdk = {extras = ["anthropic", "fastapi", "launchdarkly", "openai", "sqlalchemy"], version = "^2.33.2"} sqlalchemy = "^2.0.40" diff --git a/autogpt_platform/docker-compose.platform.yml b/autogpt_platform/docker-compose.platform.yml index bf3d17fc33..b2df626029 100644 --- a/autogpt_platform/docker-compose.platform.yml +++ b/autogpt_platform/docker-compose.platform.yml @@ -117,6 +117,11 @@ services: - "8006:8006" networks: - app-network + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" executor: build: @@ -147,6 +152,11 @@ services: - "8002:8002" networks: - app-network + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" websocket_server: build: @@ -175,6 +185,11 @@ services: - "8001:8001" networks: - app-network + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" database_manager: build: @@ -199,6 +214,11 @@ services: - "8005:8005" networks: - app-network + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" scheduler_server: build: @@ -242,6 +262,11 @@ services: - "8003:8003" networks: - app-network + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" notification_server: build: @@ -270,6 +295,11 @@ services: - "8007:8007" networks: - app-network + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" frontend: build: context: ../ @@ -286,6 +316,11 @@ services: - "3000:3000" networks: - app-network + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" # Load environment variables in order (later overrides earlier) env_file: - path: ./frontend/.env.default # Base defaults (always exists)