refactor(terminal): probe NOPASSWD only when a prompt would fire; drop the host-only probe
With BaseEnvironment supplying a backend-scoped probe to every production caller, the module-level host-only `_sudo_nopasswd_works` (and its TERMINAL_ENV gate) had no callers left; the `or` fallback and the outer try/except around a callback that already fails closed were dead too. Move the probe under `should_prompt_for_sudo`: headless callers (gateway, cron, delegated children) reach `(command, None)` whether or not the probe runs, so the extra backend round trip — an ssh exec on the SSH backend — was pure waste on every headless sudo command. test_subagent_sudo_prompt no longer needs to patch the host probe out: bare `_transform_sudo_command(cmd)` calls have no probe by construction.
This commit is contained in:
+11
-32
@@ -13,8 +13,7 @@ import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from typing import Callable
|
||||
from collections.abc import Iterator
|
||||
from collections.abc import Callable, Iterator
|
||||
|
||||
from utils import env_var_enabled
|
||||
|
||||
@@ -363,23 +362,6 @@ def _count_real_sudo_invocations(command: str) -> int:
|
||||
return _rewrite_real_sudo_invocations(command)[1]
|
||||
|
||||
|
||||
def _sudo_nopasswd_works() -> bool:
|
||||
"""True when local sudo currently works without prompting. Local backend only — Docker/SSH/
|
||||
Modal must not inherit host sudo state. Re-probes every call (no cache) so an expired sudo
|
||||
timestamp can't make a later command silently block waiting for a password."""
|
||||
from tools.terminal_tool import _tenv
|
||||
if (_tenv("TERMINAL_ENV", "local").strip().lower() or "local") != "local":
|
||||
return False
|
||||
try:
|
||||
probe = subprocess.run(
|
||||
["sudo", "-n", "true"], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL, timeout=3, check=False,
|
||||
)
|
||||
return probe.returncode == 0
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _rewrite_compound_background(command: str) -> str:
|
||||
"""Wrap `A && B &` (or `A || B &`) to `A && { B & }` at depth 0. Bash binds `&&` tighter
|
||||
than `&`, so `A && B &` backgrounds a subshell that runs B in the foreground and waits for
|
||||
@@ -447,7 +429,9 @@ def _transform_sudo_command(
|
||||
password in the command string themselves. With no password available the command is
|
||||
returned unchanged and ``sudo_stdin`` is None, so it fails gracefully with "sudo: a password
|
||||
is required". Password sources, in order: configured SUDO_PASSWORD, the session cache, then
|
||||
an interactive prompt (45s timeout, cached on success) when a UI is reachable."""
|
||||
an interactive prompt (45s timeout, cached on success) when a UI is reachable.
|
||||
``sudo_nopasswd_check`` (supplied by ``BaseEnvironment``) runs ``sudo -n true`` inside the
|
||||
selected backend; a True result skips the prompt and the ``-S`` rewrite entirely."""
|
||||
from tools.terminal_tool import _get_sudo_password_callback
|
||||
if command is None:
|
||||
return None, None
|
||||
@@ -465,24 +449,19 @@ def _transform_sudo_command(
|
||||
has_configured_password = _configured_password is not None
|
||||
sudo_password = _configured_password if has_configured_password else _get_cached_sudo_password()
|
||||
|
||||
# sudoers NOPASSWD must not be forced through the prompt or the -S pipe. BaseEnvironment
|
||||
# supplies a probe scoped to the selected backend; direct callers keep the local-host
|
||||
# fallback. Re-probed every call so an expired sudo timestamp cannot silently block.
|
||||
if not has_configured_password and not sudo_password:
|
||||
nopasswd_check = sudo_nopasswd_check or _sudo_nopasswd_works
|
||||
try:
|
||||
if nopasswd_check():
|
||||
return command, None
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# delegate_task children inherit HERMES_INTERACTIVE=1 (and possibly a stale thread-local
|
||||
# callback on a recycled worker) but have no user on the other side — always headless;
|
||||
# configured password, session cache and the NOPASSWD probe still apply.
|
||||
# configured password and session cache still apply.
|
||||
should_prompt_for_sudo = (
|
||||
env_var_enabled("HERMES_INTERACTIVE") or _get_sudo_password_callback() is not None
|
||||
) and not _in_delegated_child_context()
|
||||
if not has_configured_password and not sudo_password and should_prompt_for_sudo:
|
||||
# sudoers NOPASSWD must not be forced through the prompt or the -S pipe. The probe is
|
||||
# a round trip on the selected backend (an ssh exec for SSH), so it only runs when a
|
||||
# prompt would otherwise fire: headless callers end up at ``(command, None)`` either
|
||||
# way. Re-probed every call so an expired sudo timestamp cannot silently block.
|
||||
if sudo_nopasswd_check is not None and sudo_nopasswd_check():
|
||||
return command, None
|
||||
sudo_password = _prompt_for_sudo_password(timeout_seconds=45)
|
||||
if sudo_password:
|
||||
_set_cached_sudo_password(sudo_password)
|
||||
|
||||
Reference in New Issue
Block a user