From 78f4c132d9749640f6cdcf7681494e3486e129de Mon Sep 17 00:00:00 2001 From: AxelRay Date: Thu, 23 Jul 2026 22:58:01 +0700 Subject: [PATCH] fix(exec): extract absolute paths after equals sign in shell guard (#4594) --- nanobot/agent/tools/shell.py | 4 ++-- tests/tools/test_tool_validation.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/nanobot/agent/tools/shell.py b/nanobot/agent/tools/shell.py index 4950925a..9fa639a7 100644 --- a/nanobot/agent/tools/shell.py +++ b/nanobot/agent/tools/shell.py @@ -918,6 +918,6 @@ class ExecTool(Tool): r"(?<;]*|\\\\[^\s\"'|><;]+(?:\\[^\s\"'|><;]+)*)", command ) - posix_paths = re.findall(r"(?:^|[\s|>'\"])(/[^\s\"'>;|<]+)", command) # POSIX: /absolute only - home_paths = re.findall(r"(?:^|[\s>'\"])(~[^\s\"'>;|<]*)", command) # POSIX/Windows home shortcut: ~ + posix_paths = re.findall(r"(?:^|[\s|>='\"])(/[^\s\"'>;|<]+)", command) # POSIX: /absolute only + home_paths = re.findall(r"(?:^|[\s>='\"])(~[/+][^\s\"'>;|<]*)", command) # POSIX/Windows home shortcut: ~/ or ~+ return win_paths + posix_paths + home_paths diff --git a/tests/tools/test_tool_validation.py b/tests/tools/test_tool_validation.py index 344fd3d0..e05c7de4 100644 --- a/tests/tools/test_tool_validation.py +++ b/tests/tools/test_tool_validation.py @@ -289,6 +289,19 @@ def test_exec_extract_absolute_paths_captures_home_paths() -> None: assert "~/out.txt" in paths +def test_exec_extract_absolute_paths_captures_paths_after_equals() -> None: + cmd = "curl --output=/etc/passwd --config=~/.nanobot/config.json" + paths = ExecTool._extract_absolute_paths(cmd) + assert "/etc/passwd" in paths + assert "~/.nanobot/config.json" in paths + + +def test_exec_extract_absolute_paths_does_not_capture_query_tilde() -> None: + cmd = 'python query.py --query \'{job=~"app"}\'' + paths = ExecTool._extract_absolute_paths(cmd) + assert not any(p.startswith("~") for p in paths) + + def test_exec_extract_absolute_paths_captures_quoted_paths() -> None: cmd = 'cat "/tmp/data.txt" "~/.nanobot/config.json"' paths = ExecTool._extract_absolute_paths(cmd) @@ -306,6 +319,15 @@ def test_exec_guard_blocks_home_path_outside_workspace(tmp_path) -> None: assert "hard policy boundary" in error +def test_exec_guard_blocks_equals_home_path_outside_workspace(tmp_path) -> None: + tool = ExecTool(restrict_to_workspace=True) + error = tool._guard_command("cat --config=~/.nanobot/config.json", str(tmp_path)) + assert error is not None + assert error.startswith( + "Error: Command blocked by safety guard (path outside working dir)" + ) + + def test_exec_guard_blocks_quoted_home_path_outside_workspace(tmp_path) -> None: tool = ExecTool(restrict_to_workspace=True) error = tool._guard_command('cat "~/.nanobot/config.json"', str(tmp_path))