From 4b92a8cd3161c51ee38ee9f4b229e8617e702276 Mon Sep 17 00:00:00 2001 From: dsad Date: Wed, 1 Jul 2026 13:03:21 +0300 Subject: [PATCH] fix(approval): detect Windows destructive shell commands --- tests/tools/test_approval.py | 50 ++++++++++++++++++++++++++++++++++++ tools/approval.py | 6 +++++ 2 files changed, 56 insertions(+) diff --git a/tests/tools/test_approval.py b/tests/tools/test_approval.py index 33c948671..8b5509c3a 100644 --- a/tests/tools/test_approval.py +++ b/tests/tools/test_approval.py @@ -83,6 +83,56 @@ class TestDetectDangerousRm: assert "delete" in desc.lower() +class TestWindowsShellDestructiveCommands: + def test_cmd_del_requires_approval(self): + dangerous, key, desc = detect_dangerous_command( + r"cmd /c del /f /q C:\tmp\hermes-victim\file.txt" + ) + assert dangerous is True + assert key is not None + assert desc == "Windows cmd destructive delete" + + def test_cmd_rmdir_requires_approval(self): + dangerous, key, desc = detect_dangerous_command( + r"cmd.exe /k rmdir /s /q C:\tmp\hermes-victim" + ) + assert dangerous is True + assert key is not None + assert desc == "Windows cmd destructive delete" + + def test_powershell_remove_item_requires_approval(self): + dangerous, key, desc = detect_dangerous_command( + r"powershell -NoProfile -Command Remove-Item -Recurse -Force C:\tmp\hermes-victim" + ) + assert dangerous is True + assert key is not None + assert desc == "Windows PowerShell destructive delete" + + def test_pwsh_rm_alias_requires_approval(self): + dangerous, key, desc = detect_dangerous_command( + r"pwsh -c rm -Recurse -Force C:\tmp\hermes-victim" + ) + assert dangerous is True + assert key is not None + assert "delete" in desc.lower() + + def test_powershell_encoded_command_requires_approval(self): + dangerous, key, desc = detect_dangerous_command( + "powershell -EncodedCommand SQBFAFgA" + ) + assert dangerous is True + assert key is not None + assert desc == "PowerShell encoded command execution" + + def test_plain_text_does_not_trigger_windows_delete(self): + dangerous, key, desc = detect_dangerous_command( + "echo remember to del old notes" + ) + assert dangerous is False + assert key is None + assert desc is None + + class TestDetectDangerousSudo: def test_shell_via_c_flag(self): is_dangerous, key, desc = detect_dangerous_command("bash -c 'echo pwned'") diff --git a/tools/approval.py b/tools/approval.py index 2797f4a7a..47667a4a2 100644 --- a/tools/approval.py +++ b/tools/approval.py @@ -499,6 +499,12 @@ DANGEROUS_PATTERNS = [ (r'\brm\s+(-[^\s]*\s+)*/', "delete in root path"), (r'\brm\s+-[^\s]*r', "recursive delete"), (r'\brm\s+--recursive\b', "recursive delete (long flag)"), + # Windows shell front-ends have destructive built-ins that do not look like + # Unix `rm`. Gate only when they are executed through cmd/powershell so + # ordinary prose or filenames containing "del"/"rd" do not trip the guard. + (r'\bcmd(?:\.exe)?\s+/(?:c|k)\s+.*\b(?:del|erase|rd|rmdir)\b', "Windows cmd destructive delete"), + (r'\b(?:powershell|pwsh)(?:\.exe)?\b.*\s-(?:command|c)\b.*\b(?:remove-item|del|erase|rd|rmdir|rm)\b', "Windows PowerShell destructive delete"), + (r'\b(?:powershell|pwsh)(?:\.exe)?\b.*\s-(?:encodedcommand|enc|e)\b', "PowerShell encoded command execution"), (r'\bchmod\s+(-[^\s]*\s+)*(777|666|o\+[rwx]*w|a\+[rwx]*w)\b', "world/other-writable permissions"), (r'\bchmod\s+--recursive\b.*(777|666|o\+[rwx]*w|a\+[rwx]*w)', "recursive world/other-writable (long flag)"), (r'\bchown\s+(-[^\s]*)?R\s+root', "recursive chown to root"),