[Security] exec.allowPatterns shell-chain bypass allows unintended command execution

Closes #5306
This commit is contained in:
Bobby
2026-08-13 11:05:04 +08:00
committed by chengyongru
parent e07ecc8cc5
commit af582246f1
2 changed files with 28 additions and 1 deletions
+3 -1
View File
@@ -993,7 +993,9 @@ class ExecTool(Tool):
):
current.append(ch)
operator_len = 1
elif ch in {";", "|"}:
# A newline separates commands just like ";" does, so a payload
# smuggled onto its own line must be checked on its own too.
elif ch in {";", "|", "\n", "\r"}:
operator_len = 1
if operator_len:
+25
View File
@@ -76,6 +76,31 @@ def test_guard_allow_patterns_block_single_ampersand_chained_segment():
assert "allowlist" in result.lower()
def test_guard_allow_patterns_block_newline_chained_segment():
"""A newline separates commands, so each line must match on its own."""
tool = ExecTool(allow_patterns=[r"echo\s+allowlisted\s*.*"])
result = tool._guard_command("echo allowlisted\ntouch /tmp/evil", "/tmp")
assert result is not None
assert "allowlist" in result.lower()
def test_guard_newline_chained_segment_still_hits_deny_patterns():
"""An allowlisted first line does not exempt a denied later line."""
tool = ExecTool(allow_patterns=[r"echo\s+allowlisted\s*.*"])
result = tool._guard_command("echo allowlisted\nrm -rf /", "/tmp")
assert result is not None
assert "deny pattern filter" in result.lower()
def test_split_shell_segments_keep_line_continuation_intact():
"""A backslash-escaped newline continues one command, not a new segment."""
assert ExecTool._split_shell_segments("echo allowlisted \\\nextra") == [
"echo allowlisted \\\nextra"
]
def test_guard_allow_patterns_preserve_trailing_background_operator():
tool = ExecTool(allow_patterns=[r"echo\s+allowlisted"])