Files
hermes-agent/tests/tools/test_terminal_tool_exception_redaction.py
Teknium 72eda946be fix(security): redact terminal exception results and ACP stderr logs (#77484)
Closes the last two emission gaps from #77484:

- tools/terminal_tool.py: both exception paths (generic except and
  TERMINAL_DEGRADED_MODE=fail) returned raw str(e) + traceback.format_exc()
  to the model — only the logger copy was redacted. Exception text can
  embed the failing command line and any secrets inline in it; both fields
  now pass through redact_sensitive_text.
- acp_adapter/entry.py: _setup_logging cleared root handlers and installed
  a plain logging.Formatter, bypassing redaction entirely on ACP stderr.
  Now uses RedactingFormatter like every other logging surface.

The other three gaps from #77484 (process(list), *_KEY regex variants,
control-char splits) were fixed in #80964/#80965.
2026-08-08 04:19:49 -07:00

45 lines
1.7 KiB
Python

"""Terminal-tool exception paths must redact secrets before returning to the model.
The logger copy of the traceback already goes through RedactingFormatter, but
the JSON result returned to the model previously carried raw ``str(e)`` and
``traceback.format_exc()`` — exception text can embed the failing command line
(and any secrets inline in it). See issue #77484.
"""
import json
import tools.terminal_tool as terminal_tool
SECRET = "sk-proj-AbCdEf1234567890SecretValue999"
def _force_exception(monkeypatch, exc):
def boom():
raise exc
monkeypatch.setattr(terminal_tool, "_get_env_config", boom)
def test_generic_exception_result_redacts_error_and_traceback(monkeypatch):
_force_exception(monkeypatch, RuntimeError(f"connect failed OPENAI_API_KEY={SECRET}"))
result = json.loads(terminal_tool.terminal_tool("echo hi"))
assert result["status"] == "error"
assert SECRET not in result["error"]
assert SECRET not in result["traceback"]
# The redaction must mask the value, not drop the message entirely.
assert "OPENAI_API_KEY=" in result["error"]
assert "Failed to execute command" in result["error"]
def test_degraded_fail_mode_result_redacts_error_and_traceback(monkeypatch):
from tools.environments.base import EnvironmentConnectionError
monkeypatch.setenv("TERMINAL_DEGRADED_MODE", "fail")
exc = EnvironmentConnectionError(
f"ssh auth failed TOKEN={SECRET}", retry_hint="retry later"
)
_force_exception(monkeypatch, exc)
result = json.loads(terminal_tool.terminal_tool("echo hi"))
assert result["status"] == "error"
assert SECRET not in result["error"]
assert SECRET not in result["traceback"]