mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-18 09:51:59 +00:00
fix(approval): detect absolute home shell rc writes
This commit is contained in:
parent
da28d5d113
commit
abd69b8117
2 changed files with 67 additions and 0 deletions
|
|
@ -369,6 +369,12 @@ class TestTeePattern:
|
|||
assert dangerous is True
|
||||
assert key is not None
|
||||
|
||||
def test_tee_absolute_home_bashrc(self):
|
||||
bashrc = Path.home() / ".bashrc"
|
||||
dangerous, key, desc = detect_dangerous_command(f"echo x | tee {bashrc}")
|
||||
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
|
||||
|
|
@ -560,11 +566,37 @@ class TestSensitiveRedirectPattern:
|
|||
assert dangerous is True
|
||||
assert key is not None
|
||||
|
||||
def test_append_to_absolute_home_ssh_authorized_keys(self):
|
||||
authorized_keys = Path.home() / ".ssh" / "authorized_keys"
|
||||
dangerous, key, desc = detect_dangerous_command(f"cat key >> {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_absolute_home_bashrc(self):
|
||||
bashrc = Path.home() / ".bashrc"
|
||||
dangerous, key, desc = detect_dangerous_command(f"echo 'alias ll=\"ls -la\"' > {bashrc}")
|
||||
assert dangerous is True
|
||||
assert key is not None
|
||||
|
||||
def test_redirect_to_home_set_after_import(self, monkeypatch, tmp_path):
|
||||
late_home = tmp_path / "late-home"
|
||||
late_home.mkdir()
|
||||
monkeypatch.setenv("HOME", str(late_home))
|
||||
|
||||
dangerous, key, desc = detect_dangerous_command(f"echo x > {late_home}/.bashrc")
|
||||
assert dangerous is True
|
||||
assert key is not None
|
||||
|
||||
def test_redirect_to_other_absolute_home_bashrc_is_not_current_user_sensitive(self):
|
||||
dangerous, key, desc = detect_dangerous_command("echo x > /tmp/not-current-home/.bashrc")
|
||||
assert dangerous is False
|
||||
assert key is None
|
||||
|
||||
def test_redirect_to_safe_tmp_file(self):
|
||||
dangerous, key, desc = detect_dangerous_command("echo hello > /tmp/output.txt")
|
||||
assert dangerous is False
|
||||
|
|
|
|||
|
|
@ -561,6 +561,11 @@ def _normalize_command_for_detection(command: str) -> str:
|
|||
command = re.sub(r'\\([^\n])', r'\1', command)
|
||||
# Strip empty-string literals that split tokens: r''m → rm, r"\"m → rm.
|
||||
command = re.sub(r"''|\"\"", '', command)
|
||||
# Fold the current user's resolved absolute home path into ~/ at detection
|
||||
# time so static user-sensitive patterns catch /home/alice/.bashrc the same
|
||||
# way they catch ~/.bashrc. Do not snapshot this at import time: tests and
|
||||
# profile/session launchers can set HOME after this module is imported.
|
||||
command = _rewrite_resolved_user_home(command)
|
||||
# Fold the resolved absolute active-profile home path into the canonical
|
||||
# ~/.hermes/ form so the Hermes config/env patterns catch it. In Docker and
|
||||
# gateway deployments the agent often references the resolved absolute path
|
||||
|
|
@ -572,6 +577,36 @@ def _normalize_command_for_detection(command: str) -> str:
|
|||
return command
|
||||
|
||||
|
||||
def _rewrite_resolved_user_home(command: str) -> str:
|
||||
"""Rewrite the current user's absolute home prefix to ``~/``.
|
||||
|
||||
Resolves HOME at detection time, including its symlink-resolved form, so
|
||||
terminal commands targeting absolute home paths are checked by the same
|
||||
static patterns as tilde and $HOME forms. No-op when HOME is unset or
|
||||
degenerate.
|
||||
"""
|
||||
try:
|
||||
home = os.path.expanduser("~")
|
||||
candidates = [
|
||||
home.rstrip("/"),
|
||||
os.path.realpath(home).rstrip("/"),
|
||||
]
|
||||
except Exception:
|
||||
return command
|
||||
seen: set[str] = set()
|
||||
for path in candidates:
|
||||
if not path or path in seen:
|
||||
continue
|
||||
seen.add(path)
|
||||
# Require an absolute path below root so a bad HOME cannot rewrite the
|
||||
# whole filesystem namespace.
|
||||
normalized = path.rstrip("/")
|
||||
if not normalized.startswith("/") or normalized.count("/") < 2:
|
||||
continue
|
||||
command = command.replace(normalized + "/", "~/")
|
||||
return command
|
||||
|
||||
|
||||
def _rewrite_resolved_hermes_home(command: str) -> str:
|
||||
"""Rewrite the resolved absolute Hermes home prefix to ``~/.hermes/``.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue