fix(security): catch sensitive path writes in approval checks (#3859)

Co-authored-by: Gutslabs <gutslabsxyz@gmail.com>
This commit is contained in:
Teknium
2026-03-29 20:57:57 -07:00
committed by GitHub
parent aa389924ad
commit 2d607d36f6
2 changed files with 52 additions and 1 deletions

View File

@@ -339,6 +339,16 @@ class TestTeePattern:
assert dangerous is True
assert key is not None
def test_tee_custom_hermes_home_env(self):
dangerous, key, desc = detect_dangerous_command("echo x | tee $HERMES_HOME/.env")
assert dangerous is True
assert key is not None
def test_tee_quoted_custom_hermes_home_env(self):
dangerous, key, desc = detect_dangerous_command('echo x | tee "$HERMES_HOME/.env"')
assert dangerous is True
assert key is not None
def test_tee_tmp_safe(self):
dangerous, key, desc = detect_dangerous_command("echo hello | tee /tmp/output.txt")
assert dangerous is False
@@ -374,6 +384,30 @@ class TestFindExecFullPathRm:
assert key is None
class TestSensitiveRedirectPattern:
"""Detect shell redirection writes to sensitive user-managed paths."""
def test_redirect_to_custom_hermes_home_env(self):
dangerous, key, desc = detect_dangerous_command("echo x > $HERMES_HOME/.env")
assert dangerous is True
assert key is not None
def test_append_to_home_ssh_authorized_keys(self):
dangerous, key, desc = detect_dangerous_command("cat key >> $HOME/.ssh/authorized_keys")
assert dangerous is True
assert key is not None
def test_append_to_tilde_ssh_authorized_keys(self):
dangerous, key, desc = detect_dangerous_command("cat key >> ~/.ssh/authorized_keys")
assert dangerous is True
assert key is not None
def test_redirect_to_safe_tmp_file(self):
dangerous, key, desc = detect_dangerous_command("echo hello > /tmp/output.txt")
assert dangerous is False
assert key is None
class TestPatternKeyUniqueness:
"""Bug: pattern_key is derived by splitting on \\b and taking [1], so
patterns starting with the same word (e.g. find -exec rm and find -delete)
@@ -606,3 +640,4 @@ class TestNormalizationBypass:
dangerous, key, desc = detect_dangerous_command(cmd)
assert dangerous is False

View File

@@ -18,6 +18,21 @@ from typing import Optional
logger = logging.getLogger(__name__)
# Sensitive write targets that should trigger approval even when referenced
# via shell expansions like $HOME or $HERMES_HOME.
_SSH_SENSITIVE_PATH = r'(?:~|\$home|\$\{home\})/\.ssh(?:/|$)'
_HERMES_ENV_PATH = (
r'(?:~\/\.hermes/|'
r'(?:\$home|\$\{home\})/\.hermes/|'
r'(?:\$hermes_home|\$\{hermes_home\})/)'
r'\.env\b'
)
_SENSITIVE_WRITE_TARGET = (
r'(?:/etc/|/dev/sd|'
rf'{_SSH_SENSITIVE_PATH}|'
rf'{_HERMES_ENV_PATH})'
)
# =========================================================================
# Dangerous command patterns
# =========================================================================
@@ -46,7 +61,8 @@ DANGEROUS_PATTERNS = [
(r'\b(python[23]?|perl|ruby|node)\s+-[ec]\s+', "script execution via -e/-c flag"),
(r'\b(curl|wget)\b.*\|\s*(ba)?sh\b', "pipe remote content to shell"),
(r'\b(bash|sh|zsh|ksh)\s+<\s*<?\s*\(\s*(curl|wget)\b', "execute remote script via process substitution"),
(r'\btee\b.*(/etc/|/dev/sd|\.ssh/|\.hermes/\.env)', "overwrite system file via tee"),
(rf'\btee\b.*["\']?{_SENSITIVE_WRITE_TARGET}', "overwrite system file via tee"),
(rf'>>?\s*["\']?{_SENSITIVE_WRITE_TARGET}', "overwrite system file via redirection"),
(r'\bxargs\s+.*\brm\b', "xargs with rm"),
(r'\bfind\b.*-exec\s+(/\S*/)?rm\b', "find -exec rm"),
(r'\bfind\b.*-delete\b', "find -delete"),