fix(env-flags): widen truthy-only session env checks to sibling sites

Build on @aydnOktay's cronjob fix by routing the cronjob check through
the shared 'env_var_enabled' helper in utils.py (same truthy set:
1/true/yes/on) and applying the same semantics to the 8 sibling call
sites that read HERMES_INTERACTIVE / HERMES_GATEWAY_SESSION /
HERMES_EXEC_ASK / HERMES_CRON_SESSION with bare os.getenv() truthy
checks:

- tools/approval.py: _is_gateway_approval_context (2), check_command_safety (2),
  check_all_command_guards (3) -- 7 sites total
- tools/terminal_tool.py: _handle_sudo_failure, sudo password prompt -- 2 sites
- tools/skills_tool.py: _is_gateway_surface -- 1 site

Without this, a user who exports HERMES_INTERACTIVE=0 in their shell
still gets interactive sudo prompts, approval prompts, and gateway
skill-install paths -- only the cronjob tool was hardened. Now all
consumers agree on the same false-like values.

Also drops the duplicate _is_truthy_env helper from cronjob_tools.py
in favour of the existing canonical utils.env_var_enabled.

Tests: extend the parametrized regression coverage to all three
session env vars (HERMES_INTERACTIVE / HERMES_GATEWAY_SESSION /
HERMES_EXEC_ASK) symmetrically. tests/tools/test_cronjob_tools.py:
60/60 pass; tests/tools/{approval,terminal_tool,skills_tool,
cron_approval_mode,hardline_blocklist}.py: 378/378 pass.
This commit is contained in:
teknium1 2026-05-15 02:03:49 -07:00 committed by Teknium
parent 734aa0f367
commit 931caf2b2d
5 changed files with 40 additions and 24 deletions

View file

@ -19,7 +19,7 @@ import unicodedata
from typing import Optional
from hermes_cli.config import cfg_get
from utils import is_truthy_value
from utils import env_var_enabled, is_truthy_value
logger = logging.getLogger(__name__)
@ -108,9 +108,9 @@ def _is_gateway_approval_context() -> bool:
fall through to the gateway branch would submit a pending approval
with no listener and block the job indefinitely.
"""
if os.getenv("HERMES_CRON_SESSION"):
if env_var_enabled("HERMES_CRON_SESSION"):
return False
if os.getenv("HERMES_GATEWAY_SESSION"):
if env_var_enabled("HERMES_GATEWAY_SESSION"):
return True
return bool(_get_session_platform())
@ -928,12 +928,12 @@ def check_dangerous_command(command: str, env_type: str,
if is_approved(session_key, pattern_key):
return {"approved": True, "message": None}
is_cli = os.getenv("HERMES_INTERACTIVE")
is_cli = env_var_enabled("HERMES_INTERACTIVE")
is_gateway = _is_gateway_approval_context()
if not is_cli and not is_gateway:
# Cron sessions: respect cron_mode config
if os.getenv("HERMES_CRON_SESSION"):
if env_var_enabled("HERMES_CRON_SESSION"):
if _get_cron_approval_mode() == "deny":
return {
"approved": False,
@ -947,7 +947,7 @@ def check_dangerous_command(command: str, env_type: str,
}
return {"approved": True, "message": None}
if is_gateway or os.getenv("HERMES_EXEC_ASK"):
if is_gateway or env_var_enabled("HERMES_EXEC_ASK"):
submit_pending(session_key, {
"command": command,
"pattern_key": pattern_key,
@ -1056,15 +1056,15 @@ def check_all_command_guards(command: str, env_type: str,
if is_truthy_value(os.getenv("HERMES_YOLO_MODE")) or is_current_session_yolo_enabled() or approval_mode == "off":
return {"approved": True, "message": None}
is_cli = os.getenv("HERMES_INTERACTIVE")
is_cli = env_var_enabled("HERMES_INTERACTIVE")
is_gateway = _is_gateway_approval_context()
is_ask = os.getenv("HERMES_EXEC_ASK")
is_ask = env_var_enabled("HERMES_EXEC_ASK")
# Preserve the existing non-interactive behavior: outside CLI/gateway/ask
# flows, we do not block on approvals and we skip external guard work.
if not is_cli and not is_gateway and not is_ask:
# Cron sessions: respect cron_mode config
if os.getenv("HERMES_CRON_SESSION"):
if env_var_enabled("HERMES_CRON_SESSION"):
if _get_cron_approval_mode() == "deny":
# Run detection to get a description for the block message
is_dangerous, _pk, description = detect_dangerous_command(command)