diff --git a/autogpt/workspace/workspace.py b/autogpt/workspace/workspace.py index b06fa9eb0..91d2140e4 100644 --- a/autogpt/workspace/workspace.py +++ b/autogpt/workspace/workspace.py @@ -15,6 +15,8 @@ from pathlib import Path class Workspace: """A class that represents a workspace for an AutoGPT agent.""" + NULL_BYTES = ["\0", "\000", "\x00", r"\z", "\u0000", "%00"] + def __init__(self, workspace_root: str | Path, restrict_to_workspace: bool): self._root = self._sanitize_path(workspace_root) self._restrict_to_workspace = restrict_to_workspace @@ -100,6 +102,13 @@ class Workspace: """ + # Posix systems disallow null bytes in paths. Windows is agnostic about it. + # Do an explicit check here for all sorts of null byte representations. + + for null_byte in Workspace.NULL_BYTES: + if null_byte in str(relative_path) or null_byte in str(root): + raise ValueError("embedded null byte") + if root is None: return Path(relative_path).resolve() diff --git a/tests/test_workspace.py b/tests/test_workspace.py index 33c096d31..fbe14d8cb 100644 --- a/tests/test_workspace.py +++ b/tests/test_workspace.py @@ -1,3 +1,4 @@ +import itertools from pathlib import Path import pytest @@ -17,26 +18,38 @@ _ACCESSIBLE_PATHS = [ Path("test_folder/../test_folder/test_file.txt"), ] -_INACCESSIBLE_PATHS = [ - # Takes us out of the workspace - Path(".."), - Path("../test_file.txt"), - Path("../not_auto_gpt_workspace"), - Path("../not_auto_gpt_workspace/test_file.txt"), - Path("test_folder/../.."), - Path("test_folder/../../test_file.txt"), - Path("test_folder/../../not_auto_gpt_workspace"), - Path("test_folder/../../not_auto_gpt_workspace/test_file.txt"), - # Contains null bytes - Path("\x00"), - Path("\x00test_file.txt"), - Path("test_folder/\x00"), - Path("test_folder/\x00test_file.txt"), - # Absolute paths - Path("/"), - Path("/test_file.txt"), - Path("/home"), -] +_INACCESSIBLE_PATHS = ( + [ + # Takes us out of the workspace + Path(".."), + Path("../test_file.txt"), + Path("../not_auto_gpt_workspace"), + Path("../not_auto_gpt_workspace/test_file.txt"), + Path("test_folder/../.."), + Path("test_folder/../../test_file.txt"), + Path("test_folder/../../not_auto_gpt_workspace"), + Path("test_folder/../../not_auto_gpt_workspace/test_file.txt"), + ] + + [ + # Contains null bytes + Path(template.format(null_byte=null_byte)) + for template, null_byte in itertools.product( + [ + "{null_byte}", + "{null_byte}test_file.txt", + "test_folder/{null_byte}", + "test_folder/{null_byte}test_file.txt", + ], + Workspace.NULL_BYTES, + ) + ] + + [ + # Absolute paths + Path("/"), + Path("/test_file.txt"), + Path("/home"), + ] +) @pytest.fixture()