mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-28 11:32:22 +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 dangerous is True
|
||||||
assert key is not None
|
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):
|
def test_tee_custom_hermes_home_env(self):
|
||||||
dangerous, key, desc = detect_dangerous_command("echo x | tee $HERMES_HOME/.env")
|
dangerous, key, desc = detect_dangerous_command("echo x | tee $HERMES_HOME/.env")
|
||||||
assert dangerous is True
|
assert dangerous is True
|
||||||
|
|
@ -560,11 +566,37 @@ class TestSensitiveRedirectPattern:
|
||||||
assert dangerous is True
|
assert dangerous is True
|
||||||
assert key is not None
|
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):
|
def test_append_to_tilde_ssh_authorized_keys(self):
|
||||||
dangerous, key, desc = detect_dangerous_command("cat key >> ~/.ssh/authorized_keys")
|
dangerous, key, desc = detect_dangerous_command("cat key >> ~/.ssh/authorized_keys")
|
||||||
assert dangerous is True
|
assert dangerous is True
|
||||||
assert key is not None
|
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):
|
def test_redirect_to_safe_tmp_file(self):
|
||||||
dangerous, key, desc = detect_dangerous_command("echo hello > /tmp/output.txt")
|
dangerous, key, desc = detect_dangerous_command("echo hello > /tmp/output.txt")
|
||||||
assert dangerous is False
|
assert dangerous is False
|
||||||
|
|
|
||||||
|
|
@ -561,6 +561,11 @@ def _normalize_command_for_detection(command: str) -> str:
|
||||||
command = re.sub(r'\\([^\n])', r'\1', command)
|
command = re.sub(r'\\([^\n])', r'\1', command)
|
||||||
# Strip empty-string literals that split tokens: r''m → rm, r"\"m → rm.
|
# Strip empty-string literals that split tokens: r''m → rm, r"\"m → rm.
|
||||||
command = re.sub(r"''|\"\"", '', command)
|
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
|
# Fold the resolved absolute active-profile home path into the canonical
|
||||||
# ~/.hermes/ form so the Hermes config/env patterns catch it. In Docker and
|
# ~/.hermes/ form so the Hermes config/env patterns catch it. In Docker and
|
||||||
# gateway deployments the agent often references the resolved absolute path
|
# gateway deployments the agent often references the resolved absolute path
|
||||||
|
|
@ -572,6 +577,36 @@ def _normalize_command_for_detection(command: str) -> str:
|
||||||
return command
|
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:
|
def _rewrite_resolved_hermes_home(command: str) -> str:
|
||||||
"""Rewrite the resolved absolute Hermes home prefix to ``~/.hermes/``.
|
"""Rewrite the resolved absolute Hermes home prefix to ``~/.hermes/``.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue