diff --git a/crewai_tools/tools/code_interpreter_tool/README.md b/crewai_tools/tools/code_interpreter_tool/README.md index ab0cbf44..91179f05 100644 --- a/crewai_tools/tools/code_interpreter_tool/README.md +++ b/crewai_tools/tools/code_interpreter_tool/README.md @@ -5,8 +5,10 @@ This tool is used to give the Agent the ability to run code (Python3) from the c It is incredible useful since it allows the Agent to generate code, run it in the same environment, get the result and use it to make decisions. +> [!CAUTION] +> By default, the CodeInterpreterTool will mount and provide read/write access to your current working directory. This means the executed code can access, modify, or delete any files in your project directory. To limit access to a specific folder, use the `mount_path` parameter to specify which directory should be mounted instead. +> ## Requirements - - Docker ## Installation @@ -51,3 +53,14 @@ Agent( ) ``` + +To limit file system access to a specific directory, use the `mount_path` parameter: + +```python +from crewai_tools import CodeInterpreterTool + +Agent( + ... + tools=[CodeInterpreterTool(mount_path="./data")], # Only provides access to ./data folder +) +``` diff --git a/crewai_tools/tools/code_interpreter_tool/code_interpreter_tool.py b/crewai_tools/tools/code_interpreter_tool/code_interpreter_tool.py index 95559f2a..11be2122 100644 --- a/crewai_tools/tools/code_interpreter_tool/code_interpreter_tool.py +++ b/crewai_tools/tools/code_interpreter_tool/code_interpreter_tool.py @@ -7,6 +7,7 @@ import importlib.util import os +from pathlib import Path from types import ModuleType from typing import Any, Dict, List, Optional, Type @@ -142,6 +143,7 @@ class CodeInterpreterTool(BaseTool): user_dockerfile_path: Optional[str] = None user_docker_base_url: Optional[str] = None unsafe_mode: bool = False + mount_path: Optional[str] = None @staticmethod def _get_installed_package_path() -> str: @@ -222,14 +224,45 @@ def _init_docker_container(self) -> Container: """Initializes and returns a Docker container for code execution. Stops and removes any existing container with the same name before creating - a new one. Maps the current working directory to /workspace in the container. + a new one. Mounts the specified mount_path (relative to cwd) if provided, + otherwise defaults to the current working directory Returns: A Docker container object ready for code execution. """ container_name = "code-interpreter" client = docker_from_env() - current_path = os.getcwd() + current_path = Path.cwd() + + # if mount_path is provided, mount that, else mount the current working directory + mount_dir = current_path # Default to current directory + + if self.mount_path and self.mount_path.strip(): + try: + proposed_mount_dir = (current_path / self.mount_path).resolve() + + # check if the mount_dir is valid + if not proposed_mount_dir.exists() or not proposed_mount_dir.is_dir(): + Printer.print( + f"Warning: Invalid mount directory: {proposed_mount_dir}. Using current directory instead.", + color="yellow", + ) + # check if the mount_dir is within the current working directory to prevent escapes + elif ( + current_path not in proposed_mount_dir.parents + and proposed_mount_dir != current_path + ): + Printer.print( + f"Warning: {proposed_mount_dir} is outside the project root. Using current directory instead.", + color="yellow", + ) + else: + mount_dir = proposed_mount_dir + except Exception as e: + Printer.print( + f"Warning: Error resolving mount path '{self.mount_path}': {e}. Using current directory instead.", + color="yellow", + ) # Check if the container is already running try: @@ -245,7 +278,7 @@ def _init_docker_container(self) -> Container: tty=True, working_dir="/workspace", name=container_name, - volumes={current_path: {"bind": "/workspace", "mode": "rw"}}, # type: ignore + volumes={mount_dir: {"bind": "/workspace", "mode": "rw"}}, # type: ignore ) def _check_docker_available(self) -> bool: