diff --git a/tests/tools/test_skills_guard.py b/tests/tools/test_skills_guard.py index facfc53765..f9684d66ce 100644 --- a/tests/tools/test_skills_guard.py +++ b/tests/tools/test_skills_guard.py @@ -192,6 +192,22 @@ class TestScanFile: assert any(fi.category == "injection" for fi in findings) + def test_sudo_event_names_are_not_sudo_usage(self, tmp_path): + """`sudo.request` / `sudo.respond` are the gateway's secure-prompt wire events (the masked sudo + password ask). A client plugin that relays those prompts must spell them out, and they are not + a privilege escalation — only a real `sudo` invocation is.""" + events = tmp_path / "events.py" + events.write_text( + 'INPUT_EVENTS = ("approval.request", "secret.request", "sudo.request")\n' + 'RESPONSES = {"sudo.respond": "value"}\n', + encoding="utf-8", + ) + assert not any(fi.pattern_id == "sudo_usage" for fi in scan_file(events, "events.py")) + + setup = tmp_path / "setup.sh" + setup.write_text("sudo apt-get install -y jq\nsudo ./install.sh\n", encoding="utf-8") + assert [fi.line for fi in scan_file(setup, "setup.sh") if fi.pattern_id == "sudo_usage"] == [1, 2] + def test_deduplication_per_pattern_per_line(self, tmp_path): f = tmp_path / "dup.sh" f.write_text("rm -rf / && rm -rf /home\n", encoding="utf-8") diff --git a/tools/skills_guard.py b/tools/skills_guard.py index 2f4c21e551..380ac4f079 100644 --- a/tools/skills_guard.py +++ b/tools/skills_guard.py @@ -304,7 +304,11 @@ THREAT_PATTERNS = [ # `allowed-tools:` is REQUIRED frontmatter per the agent-skill spec — informational (low) only. (r'^allowed-tools\s*:', "allowed_tools_field", "low", "privilege_escalation", "skill declares allowed-tools (standard frontmatter; informational)"), - (r'\bsudo\b', "sudo_usage", "high", "privilege_escalation", "uses sudo (privilege escalation)"), + # `sudo.request` / `sudo.respond` are gateway wire events (the masked sudo-password prompt), not an + # invocation: any client plugin that relays Hermes' secure prompts has to name them, and a bare + # `\bsudo\b` made every such plugin `caution`. A dotted event name is never a shell `sudo`. + (r'\bsudo\b(?!\.(?:request|respond)\b)', + "sudo_usage", "high", "privilege_escalation", "uses sudo (privilege escalation)"), (r'setuid|setgid|cap_setuid', "setuid_setgid", "critical", "privilege_escalation", "setuid/setgid (privilege escalation mechanism)"), (r'NOPASSWD',