mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-22 10:32:00 +00:00
fix(cron): sanitize env for job script subprocesses
Cron no_agent and pre-check scripts ran with the full gateway/agent environment, allowing scripts under HERMES_HOME/scripts/ to read provider credentials. Apply _sanitize_subprocess_env like terminal and MCP paths (SECURITY.md section 2.3). Add regression test asserting blocklisted provider vars are absent in the child process.
This commit is contained in:
parent
c06898098b
commit
da7253215d
2 changed files with 30 additions and 0 deletions
|
|
@ -961,6 +961,10 @@ def _run_job_script(script_path: str) -> tuple[bool, str]:
|
|||
Shell support lets ``no_agent=True`` jobs ship classic bash watchdogs
|
||||
(the `memory-watchdog.sh` pattern) without wrapping them in Python.
|
||||
|
||||
Subprocess environment is passed through ``_sanitize_subprocess_env`` so
|
||||
provider credentials and other Hermes-managed secrets are not inherited
|
||||
(SECURITY.md §2.3), matching terminal and MCP child processes.
|
||||
|
||||
Args:
|
||||
script_path: Path to the script. Relative paths are resolved
|
||||
against HERMES_HOME/scripts/. Absolute and ~-prefixed paths
|
||||
|
|
@ -1022,6 +1026,8 @@ def _run_job_script(script_path: str) -> tuple[bool, str]:
|
|||
argv = [sys.executable, str(path)]
|
||||
|
||||
try:
|
||||
from tools.environments.local import _sanitize_subprocess_env
|
||||
|
||||
popen_kwargs = {"creationflags": windows_hide_flags()} if sys.platform == "win32" else {}
|
||||
result = subprocess.run(
|
||||
argv,
|
||||
|
|
@ -1029,6 +1035,7 @@ def _run_job_script(script_path: str) -> tuple[bool, str]:
|
|||
text=True,
|
||||
timeout=script_timeout,
|
||||
cwd=str(path.parent),
|
||||
env=_sanitize_subprocess_env(os.environ),
|
||||
**popen_kwargs,
|
||||
)
|
||||
stdout = (result.stdout or "").strip()
|
||||
|
|
|
|||
|
|
@ -132,6 +132,29 @@ class TestRunJobScript:
|
|||
assert "exited with code 1" in output
|
||||
assert "error info" in output
|
||||
|
||||
def test_script_subprocess_env_sanitized(self, cron_env, monkeypatch):
|
||||
"""Cron scripts must not inherit Hermes provider env (SECURITY.md §2.3)."""
|
||||
from tools.environments.local import _HERMES_PROVIDER_ENV_BLOCKLIST
|
||||
from cron.scheduler import _run_job_script
|
||||
|
||||
blocked_var = next(iter(_HERMES_PROVIDER_ENV_BLOCKLIST))
|
||||
monkeypatch.setenv(blocked_var, "must_not_leak")
|
||||
|
||||
script = cron_env / "scripts" / "env_probe.py"
|
||||
script.write_text(
|
||||
textwrap.dedent(
|
||||
f"""\
|
||||
import os
|
||||
key = {blocked_var!r}
|
||||
print("PRESENT" if os.environ.get(key) else "ABSENT")
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
success, output = _run_job_script("env_probe.py")
|
||||
assert success is True
|
||||
assert output == "ABSENT"
|
||||
|
||||
def test_script_empty_output(self, cron_env):
|
||||
from cron.scheduler import _run_job_script
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue