fix: treat single ampersand as shell segment
Maintainer edit: single '&' backgrounds the preceding command and starts another top-level shell segment, so allowPatterns must split it the same way as ';', '|', '&&', and '||'. Keep fd redirections such as 2>&1 and &> intact.
This commit is contained in:
@@ -879,6 +879,11 @@ class ExecTool(Tool):
|
|||||||
if paren_depth == 0:
|
if paren_depth == 0:
|
||||||
if command.startswith(("&&", "||"), i):
|
if command.startswith(("&&", "||"), i):
|
||||||
operator_len = 2
|
operator_len = 2
|
||||||
|
elif ch == "&":
|
||||||
|
prev_ch = command[i - 1] if i > 0 else ""
|
||||||
|
next_ch = command[i + 1] if i + 1 < len(command) else ""
|
||||||
|
if prev_ch not in {"<", ">"} and next_ch != ">":
|
||||||
|
operator_len = 1
|
||||||
elif ch in {";", "|"}:
|
elif ch in {";", "|"}:
|
||||||
operator_len = 1
|
operator_len = 1
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,23 @@ def test_guard_allow_patterns_block_non_matching_chained_segment():
|
|||||||
assert "allowlist" in result.lower()
|
assert "allowlist" in result.lower()
|
||||||
|
|
||||||
|
|
||||||
|
def test_guard_allow_patterns_block_single_ampersand_chained_segment():
|
||||||
|
"""A backgrounded command is also a top-level shell segment."""
|
||||||
|
tool = ExecTool(allow_patterns=[r"echo\s+allowlisted.*"])
|
||||||
|
|
||||||
|
result = tool._guard_command("echo allowlisted & touch /tmp/evil", "/tmp")
|
||||||
|
assert result is not None
|
||||||
|
assert "allowlist" in result.lower()
|
||||||
|
|
||||||
|
|
||||||
|
def test_guard_allow_patterns_keep_fd_redirection_ampersand():
|
||||||
|
tool = ExecTool(allow_patterns=[r"echo\s+allowlisted\s+2>&1"])
|
||||||
|
|
||||||
|
result = tool._guard_command("echo allowlisted 2>&1", "/tmp")
|
||||||
|
|
||||||
|
assert result is None
|
||||||
|
|
||||||
|
|
||||||
def test_deny_patterns_search_original_command_with_quoted_hash():
|
def test_deny_patterns_search_original_command_with_quoted_hash():
|
||||||
"""Deny checks must still inspect text after a quoted hash."""
|
"""Deny checks must still inspect text after a quoted hash."""
|
||||||
tool = ExecTool(deny_patterns=[r"\brm\s+-rf\s+/"])
|
tool = ExecTool(deny_patterns=[r"\brm\s+-rf\s+/"])
|
||||||
|
|||||||
Reference in New Issue
Block a user