mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-21 14:22:19 +02:00
fix(security): confine workspace existence checks
This commit is contained in:
+5
-6
@@ -16711,16 +16711,15 @@ def _existing_workspace_files(paths: list[str], workspace: Optional[str]) -> lis
|
|||||||
"""
|
"""
|
||||||
if not workspace:
|
if not workspace:
|
||||||
return []
|
return []
|
||||||
root = Path(str(workspace)).expanduser()
|
from src.tool_execution import _resolve_tool_path_in_workspace
|
||||||
|
|
||||||
existing: list[str] = []
|
existing: list[str] = []
|
||||||
for path in paths:
|
for path in paths:
|
||||||
candidate = Path(path).expanduser()
|
|
||||||
if not candidate.is_absolute():
|
|
||||||
candidate = root / candidate
|
|
||||||
try:
|
try:
|
||||||
if candidate.is_file():
|
resolved = _resolve_tool_path_in_workspace(str(workspace), str(path))
|
||||||
|
if os.path.isfile(resolved):
|
||||||
existing.append(path)
|
existing.append(path)
|
||||||
except OSError:
|
except (OSError, ValueError):
|
||||||
continue
|
continue
|
||||||
return existing
|
return existing
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
from src.agent_loop import _existing_workspace_files
|
||||||
|
|
||||||
|
|
||||||
|
def test_existing_workspace_files_accepts_relative_file_inside_workspace(tmp_path):
|
||||||
|
workspace = tmp_path / "workspace"
|
||||||
|
workspace.mkdir()
|
||||||
|
target = workspace / "inside.py"
|
||||||
|
target.write_text("pass\n")
|
||||||
|
|
||||||
|
assert _existing_workspace_files(
|
||||||
|
["inside.py"],
|
||||||
|
str(workspace),
|
||||||
|
) == ["inside.py"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_existing_workspace_files_accepts_absolute_file_inside_workspace(tmp_path):
|
||||||
|
workspace = tmp_path / "workspace"
|
||||||
|
workspace.mkdir()
|
||||||
|
target = workspace / "inside.py"
|
||||||
|
target.write_text("pass\n")
|
||||||
|
|
||||||
|
assert _existing_workspace_files(
|
||||||
|
[str(target)],
|
||||||
|
str(workspace),
|
||||||
|
) == [str(target)]
|
||||||
|
|
||||||
|
|
||||||
|
def test_existing_workspace_files_rejects_absolute_file_outside_workspace(tmp_path):
|
||||||
|
workspace = tmp_path / "workspace"
|
||||||
|
workspace.mkdir()
|
||||||
|
outside = tmp_path / "outside.py"
|
||||||
|
outside.write_text("pass\n")
|
||||||
|
|
||||||
|
assert _existing_workspace_files(
|
||||||
|
[str(outside)],
|
||||||
|
str(workspace),
|
||||||
|
) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_existing_workspace_files_rejects_parent_traversal(tmp_path):
|
||||||
|
workspace = tmp_path / "workspace"
|
||||||
|
workspace.mkdir()
|
||||||
|
outside = tmp_path / "outside.py"
|
||||||
|
outside.write_text("pass\n")
|
||||||
|
|
||||||
|
assert _existing_workspace_files(
|
||||||
|
["../outside.py"],
|
||||||
|
str(workspace),
|
||||||
|
) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_existing_workspace_files_rejects_symlink_escape(tmp_path):
|
||||||
|
workspace = tmp_path / "workspace"
|
||||||
|
workspace.mkdir()
|
||||||
|
outside = tmp_path / "outside.py"
|
||||||
|
outside.write_text("pass\n")
|
||||||
|
|
||||||
|
escape = workspace / "escape.py"
|
||||||
|
escape.symlink_to(outside)
|
||||||
|
|
||||||
|
assert _existing_workspace_files(
|
||||||
|
["escape.py"],
|
||||||
|
str(workspace),
|
||||||
|
) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_existing_workspace_files_supports_workspace_alias(tmp_path):
|
||||||
|
workspace = tmp_path / "workspace"
|
||||||
|
workspace.mkdir()
|
||||||
|
target = workspace / "inside.py"
|
||||||
|
target.write_text("pass\n")
|
||||||
|
|
||||||
|
assert _existing_workspace_files(
|
||||||
|
["/workspace/inside.py"],
|
||||||
|
str(workspace),
|
||||||
|
) == ["/workspace/inside.py"]
|
||||||
Reference in New Issue
Block a user