fix(shell): reject LLM-supplied working_dir outside workspace (#2826)

This commit is contained in:
04cb
2026-04-12 16:38:55 +08:00
committed by Xubin Ren
parent 00fb491bc9
commit 3f59bd1443
2 changed files with 83 additions and 0 deletions
+68
View File
@@ -113,3 +113,71 @@ def test_exec_allows_reads_of_history_jsonl(command):
tool = ExecTool()
result = tool._guard_command(command, "/tmp")
assert result is None
# --- #2826: working_dir must not escape the configured workspace ---------
@pytest.mark.asyncio
async def test_exec_blocks_working_dir_outside_workspace(tmp_path):
"""An LLM-supplied working_dir outside the workspace must be rejected."""
workspace = tmp_path / "workspace"
workspace.mkdir()
tool = ExecTool(working_dir=str(workspace), restrict_to_workspace=True)
result = await tool.execute(command="rm calendar.ics", working_dir="/etc")
assert "outside the configured workspace" in result
@pytest.mark.asyncio
async def test_exec_blocks_absolute_rm_via_hijacked_working_dir(tmp_path):
"""Regression for #2826: `rm /abs/path` via working_dir hijack."""
workspace = tmp_path / "workspace"
workspace.mkdir()
victim_dir = tmp_path / "outside"
victim_dir.mkdir()
victim = victim_dir / "file.ics"
victim.write_text("data")
tool = ExecTool(working_dir=str(workspace), restrict_to_workspace=True)
result = await tool.execute(
command=f"rm {victim}",
working_dir=str(victim_dir),
)
assert "outside the configured workspace" in result
assert victim.exists(), "victim file must not have been deleted"
@pytest.mark.asyncio
async def test_exec_allows_working_dir_within_workspace(tmp_path):
"""A working_dir that is a subdirectory of the workspace is fine."""
workspace = tmp_path / "workspace"
subdir = workspace / "project"
subdir.mkdir(parents=True)
tool = ExecTool(working_dir=str(workspace), restrict_to_workspace=True, timeout=5)
result = await tool.execute(command="echo ok", working_dir=str(subdir))
assert "ok" in result
assert "outside the configured workspace" not in result
@pytest.mark.asyncio
async def test_exec_allows_working_dir_equal_to_workspace(tmp_path):
"""Passing working_dir equal to the workspace root must be allowed."""
workspace = tmp_path / "workspace"
workspace.mkdir()
tool = ExecTool(working_dir=str(workspace), restrict_to_workspace=True, timeout=5)
result = await tool.execute(command="echo ok", working_dir=str(workspace))
assert "ok" in result
assert "outside the configured workspace" not in result
@pytest.mark.asyncio
async def test_exec_ignores_workspace_check_when_not_restricted(tmp_path):
"""Without restrict_to_workspace, the LLM may still choose any working_dir."""
workspace = tmp_path / "workspace"
workspace.mkdir()
other = tmp_path / "other"
other.mkdir()
tool = ExecTool(working_dir=str(workspace), restrict_to_workspace=False, timeout=5)
result = await tool.execute(command="echo ok", working_dir=str(other))
assert "ok" in result
assert "outside the configured workspace" not in result