From 0a6d366327432f9ac3c3463839af7238a2d3fe9a Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Tue, 31 Mar 2026 18:52:11 -0700 Subject: [PATCH] fix(security): redact secrets from execute_code sandbox output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: root-level provider in config.yaml no longer overrides model.provider load_cli_config() had a priority inversion: a stale root-level 'provider' key in config.yaml would OVERRIDE the canonical 'model.provider' set by 'hermes model'. The gateway reads model.provider directly from YAML and worked correctly, but 'hermes chat -q' and the interactive CLI went through the merge logic and picked up the stale root-level key. Fix: root-level provider/base_url are now only used as a fallback when model.provider/model.base_url is not set (never as an override). Also added _normalize_root_model_keys() to config.py load_config() and save_config() — migrates root-level provider/base_url into the model section and removes the root-level keys permanently. Reported by (≧▽≦) in Discord: opencode-go provider persisted as a root-level key and overrode the correct model.provider=openrouter, causing 401 errors. * fix(security): redact secrets from execute_code sandbox output The execute_code sandbox stripped env vars with secret-like names from the child process (preventing os.environ access), but scripts could still read secrets from disk (e.g. open('~/.hermes/.env')) and print them to stdout. The raw values entered the model context unredacted. terminal_tool and file_tools already applied redact_sensitive_text() to their output — execute_code was the only tool that skipped this step. Now the same redaction runs on both stdout and stderr after ANSI stripping. Reported via Discord (not filed on GitHub to avoid public disclosure of the reproduction steps). --- tools/code_execution_tool.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/code_execution_tool.py b/tools/code_execution_tool.py index 19270c6fe..ce78c9061 100644 --- a/tools/code_execution_tool.py +++ b/tools/code_execution_tool.py @@ -596,6 +596,14 @@ def execute_code( stdout_text = strip_ansi(stdout_text) stderr_text = strip_ansi(stderr_text) + # Redact secrets (API keys, tokens, etc.) from sandbox output. + # The sandbox env-var filter (lines 434-454) blocks os.environ access, + # but scripts can still read secrets from disk (e.g. open('~/.hermes/.env')). + # This ensures leaked secrets never enter the model context. + from agent.redact import redact_sensitive_text + stdout_text = redact_sensitive_text(stdout_text) + stderr_text = redact_sensitive_text(stderr_text) + # Build response result: Dict[str, Any] = { "status": status,