mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-03 02:11:48 +00:00
fix(terminal): skip sudo prompt when local NOPASSWD sudo works
When running on a host with sudoers NOPASSWD configured for the current user, interactive Hermes sessions were unnecessarily entering the password prompt path before executing sudo commands. Outside Hermes, `sudo -n true` exits 0 for that user. Add `_sudo_nopasswd_works()` that probes `sudo -n true` and, when it succeeds, lets `_transform_sudo_command()` return the command unchanged with no stdin password. The probe: - Is scoped to the `local` terminal backend only, so Docker/SSH/Modal and other remote backends do not inherit host sudo state. - Re-probes every call (no process-lifetime cache) so an expired sudo timestamp cannot silently make a later command block waiting for a password that Hermes never prompts for. - Is bypassed entirely when `SUDO_PASSWORD` is configured or a cached password already exists, preserving existing explicit-password flows. Co-authored-by: Junting Wu <juntingpublic@gmail.com>
This commit is contained in:
parent
ccfe6a47c3
commit
ab6c629ccc
2 changed files with 86 additions and 0 deletions
|
|
@ -104,6 +104,57 @@ def test_cached_sudo_password_isolated_by_session_key(monkeypatch):
|
|||
assert terminal_tool._get_cached_sudo_password() == "alpha-pass"
|
||||
|
||||
|
||||
def test_passwordless_sudo_skips_interactive_prompt_and_rewrite(monkeypatch):
|
||||
monkeypatch.delenv("SUDO_PASSWORD", raising=False)
|
||||
monkeypatch.delenv("TERMINAL_ENV", raising=False)
|
||||
monkeypatch.setenv("HERMES_INTERACTIVE", "1")
|
||||
|
||||
def _fail_prompt(*_args, **_kwargs):
|
||||
raise AssertionError(
|
||||
"interactive sudo prompt should not run when sudo -n already works"
|
||||
)
|
||||
|
||||
monkeypatch.setattr(terminal_tool, "_prompt_for_sudo_password", _fail_prompt)
|
||||
monkeypatch.setattr(terminal_tool, "_sudo_nopasswd_works", lambda: True, raising=False)
|
||||
|
||||
transformed, sudo_stdin = terminal_tool._transform_sudo_command("sudo whoami")
|
||||
|
||||
assert transformed == "sudo whoami"
|
||||
assert sudo_stdin is None
|
||||
|
||||
|
||||
def test_passwordless_sudo_probe_rechecks_local_terminal(monkeypatch):
|
||||
monkeypatch.delenv("TERMINAL_ENV", raising=False)
|
||||
calls = []
|
||||
|
||||
class Result:
|
||||
def __init__(self, returncode):
|
||||
self.returncode = returncode
|
||||
|
||||
def fake_run(args, **kwargs):
|
||||
calls.append((args, kwargs))
|
||||
return Result(0 if len(calls) == 1 else 1)
|
||||
|
||||
monkeypatch.setattr(terminal_tool.subprocess, "run", fake_run)
|
||||
|
||||
assert terminal_tool._sudo_nopasswd_works() is True
|
||||
assert terminal_tool._sudo_nopasswd_works() is False
|
||||
assert len(calls) == 2
|
||||
assert calls[0][0] == ["sudo", "-n", "true"]
|
||||
assert calls[1][0] == ["sudo", "-n", "true"]
|
||||
|
||||
|
||||
def test_passwordless_sudo_probe_is_disabled_for_nonlocal_terminal_env(monkeypatch):
|
||||
monkeypatch.setenv("TERMINAL_ENV", "docker")
|
||||
|
||||
def _fail_run(*_args, **_kwargs):
|
||||
raise AssertionError("host sudo probe must not run for non-local terminal envs")
|
||||
|
||||
monkeypatch.setattr(terminal_tool.subprocess, "run", _fail_run)
|
||||
|
||||
assert terminal_tool._sudo_nopasswd_works() is False
|
||||
|
||||
|
||||
def test_validate_workdir_allows_windows_drive_paths():
|
||||
assert terminal_tool._validate_workdir(r"C:\Users\Alice\project") is None
|
||||
assert terminal_tool._validate_workdir("C:/Users/Alice/project") is None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue