fix: allow git commands in workspace subdirectories
The shell safety guard in _guard_command() checked extracted absolute paths only against cwd_path (the command's working directory). When cwd was a subdirectory like ~/.nanobot/workspace/obsidian_notes, any absolute path referencing the broader workspace (e.g. a sibling directory or the workspace root itself) was incorrectly blocked with "path outside working dir". Fix: pass the workspace_root from _prepare_command() into _guard_command() and check absolute paths against it as a fallback when they are outside cwd_path. Paths truly outside the workspace root are still blocked. Added tests: - test_exec_allows_workspace_paths_from_subdirectory - test_exec_blocks_outside_paths_from_subdirectory
This commit is contained in:
@@ -397,6 +397,7 @@ class ExecTool(Tool):
|
||||
command,
|
||||
cwd,
|
||||
restrict_to_workspace=access.restrict_to_workspace,
|
||||
workspace_root=workspace_root,
|
||||
)
|
||||
if guard_error:
|
||||
return guard_error
|
||||
@@ -591,6 +592,7 @@ class ExecTool(Tool):
|
||||
cwd: str,
|
||||
*,
|
||||
restrict_to_workspace: bool | None = None,
|
||||
workspace_root: str | None = None,
|
||||
) -> str | None:
|
||||
"""Best-effort safety guard for potentially destructive commands."""
|
||||
cmd = command.strip()
|
||||
@@ -629,6 +631,11 @@ class ExecTool(Tool):
|
||||
)
|
||||
|
||||
cwd_path = Path(cwd).resolve()
|
||||
resolved_workspace = (
|
||||
Path(workspace_root).expanduser().resolve()
|
||||
if workspace_root
|
||||
else None
|
||||
)
|
||||
|
||||
for raw in self._extract_absolute_paths(cmd):
|
||||
try:
|
||||
@@ -646,10 +653,13 @@ class ExecTool(Tool):
|
||||
continue
|
||||
|
||||
media_path = get_media_dir().resolve()
|
||||
if p.is_absolute() and not (
|
||||
allowed = (
|
||||
is_path_within(p, cwd_path)
|
||||
or is_path_within(p, media_path)
|
||||
):
|
||||
)
|
||||
if not allowed and resolved_workspace is not None:
|
||||
allowed = is_path_within(p, resolved_workspace)
|
||||
if p.is_absolute() and not allowed:
|
||||
return (
|
||||
"Error: Command blocked by safety guard (path outside working dir)"
|
||||
+ _WORKSPACE_BOUNDARY_NOTE
|
||||
|
||||
@@ -349,3 +349,49 @@ def test_exec_allows_format_in_url_and_args(command):
|
||||
tool = ExecTool()
|
||||
result = tool._guard_command(command, "/tmp")
|
||||
assert result is None
|
||||
|
||||
|
||||
# --- workspace_root allows paths inside workspace but outside cwd ----------
|
||||
|
||||
|
||||
def test_exec_allows_workspace_paths_from_subdirectory(tmp_path):
|
||||
"""Absolute paths inside the workspace root must be allowed even when cwd
|
||||
is a subdirectory. This is the scenario reported in the issue: git
|
||||
commands in ``~/.nanobot/workspace/obsidian_notes`` reference paths
|
||||
under the broader workspace that are outside the subdirectory cwd."""
|
||||
workspace = tmp_path / "workspace"
|
||||
subdir = workspace / "obsidian_notes"
|
||||
subdir.mkdir(parents=True)
|
||||
sibling = workspace / "other_project"
|
||||
sibling.mkdir()
|
||||
|
||||
tool = ExecTool(working_dir=str(workspace), restrict_to_workspace=True)
|
||||
|
||||
# A command run from the subdirectory that references a sibling path
|
||||
# inside the workspace should be allowed.
|
||||
result = tool._guard_command(
|
||||
f"git clone {sibling}",
|
||||
str(subdir),
|
||||
workspace_root=str(workspace),
|
||||
)
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_exec_blocks_outside_paths_from_subdirectory(tmp_path):
|
||||
"""Paths truly outside the workspace must still be blocked even when
|
||||
workspace_root is provided."""
|
||||
workspace = tmp_path / "workspace"
|
||||
subdir = workspace / "project"
|
||||
subdir.mkdir(parents=True)
|
||||
outside = tmp_path / "secrets"
|
||||
outside.mkdir()
|
||||
|
||||
tool = ExecTool(working_dir=str(workspace), restrict_to_workspace=True)
|
||||
|
||||
result = tool._guard_command(
|
||||
f"cat {outside / 'key.pem'}",
|
||||
str(subdir),
|
||||
workspace_root=str(workspace),
|
||||
)
|
||||
assert result is not None
|
||||
assert "path outside working dir" in result
|
||||
|
||||
Reference in New Issue
Block a user