From 931624feda96be2c2a8bdd0ea48c6c5d2f3db87b Mon Sep 17 00:00:00 2001 From: memosr Date: Sat, 4 Apr 2026 16:58:15 -0700 Subject: [PATCH] fix(security): guard cron script against path traversal and redact output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Relative script paths resolved against HERMES_HOME/scripts/ were not validated to stay within that directory. Paths like '../../etc/passwd' could escape and be executed as Python. Fix: resolve the path and verify it stays within scripts_dir using Path.relative_to(). Also apply redact_sensitive_text() to script stdout before LLM injection — same pattern as execute_code sandbox output. Cherry-picked from PR #5093 by memosr (fixes 1 and 3; absolute path restriction dropped as too restrictive for the feature's design intent). --- cron/scheduler.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/cron/scheduler.py b/cron/scheduler.py index b01479983..8b977f422 100644 --- a/cron/scheduler.py +++ b/cron/scheduler.py @@ -248,7 +248,13 @@ def _run_job_script(script_path: str) -> tuple[bool, str]: path = Path(script_path).expanduser() if not path.is_absolute(): # Resolve relative paths against HERMES_HOME/scripts/ - path = get_hermes_home() / "scripts" / path + scripts_dir = get_hermes_home() / "scripts" + path = (scripts_dir / path).resolve() + # Guard against path traversal (e.g. "../../etc/passwd") + try: + path.relative_to(scripts_dir.resolve()) + except ValueError: + return False, f"Script path escapes the scripts directory: {script_path!r}" if not path.exists(): return False, f"Script not found: {path}" @@ -274,6 +280,13 @@ def _run_job_script(script_path: str) -> tuple[bool, str]: parts.append(f"stdout:\n{stdout}") return False, "\n".join(parts) + # Redact any secrets that may appear in script output before + # they are injected into the LLM prompt context. + try: + from agent.redact import redact_sensitive_text + stdout = redact_sensitive_text(stdout) + except Exception: + pass return True, stdout except subprocess.TimeoutExpired: