From ad58dd51ac173172fb85223aec5f3aeba1abfaf8 Mon Sep 17 00:00:00 2001 From: xtymac Date: Mon, 15 Jun 2026 20:39:55 +0900 Subject: [PATCH] redact secrets in API request debug dumps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dump_api_request_debug() masks the provider Authorization header but writes the request `body` (system prompt, tool defs, context-embedded values) and the error message raw via atomic_json_write. This path also fires unconditionally on API errors (not only under HERMES_DUMP_REQUESTS), so any secret surfaced into context (e.g. an integration token) lands in cleartext at request_dump_*.json on every failed call. Run the serialized dump through the existing redact_sensitive_text() scrubber (already used for logs/tool output) before persisting and before the HERMES_DUMP_REQUEST_STDOUT print; preserve atomicity via temp-file + Path.replace. Also add the Notion internal-integration prefix (ntn_) to _PREFIX_PATTERNS so bare values are caught. Per SECURITY.md §3.2 this is a redaction (in-process heuristic) hardening, not a §3.1 vulnerability. Refs #46583. --- agent/agent_runtime_helpers.py | 16 ++++++++++++++-- agent/redact.py | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/agent/agent_runtime_helpers.py b/agent/agent_runtime_helpers.py index cae1a685a53..b0ea2f62112 100644 --- a/agent/agent_runtime_helpers.py +++ b/agent/agent_runtime_helpers.py @@ -1217,12 +1217,24 @@ def dump_api_request_debug( timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f") dump_file = agent.logs_dir / f"request_dump_{agent.session_id}_{timestamp}.json" - atomic_json_write(dump_file, dump_payload, default=str) + + # Redact secrets before persisting/printing. This dump captures the + # full request body (system prompt, tool defs, context-embedded + # values), and this path fires unconditionally on API errors — so it + # otherwise lands any context-embedded secret in cleartext on disk. + # Run the serialized dump through the same scrubber used for logs/tool + # output. Atomicity preserved via temp-file + Path.replace. + from agent.redact import redact_sensitive_text + _serialized = json.dumps(dump_payload, ensure_ascii=False, indent=2, default=str) + _redacted = redact_sensitive_text(_serialized, force=True) + _tmp = dump_file.with_name(dump_file.name + ".tmp") + _tmp.write_text(_redacted, encoding="utf-8") + _tmp.replace(dump_file) agent._vprint(f"{agent.log_prefix}🧾 Request debug dump written to: {dump_file}") if env_var_enabled("HERMES_DUMP_REQUEST_STDOUT"): - print(json.dumps(dump_payload, ensure_ascii=False, indent=2, default=str)) + print(_redacted) return dump_file except Exception as dump_error: diff --git a/agent/redact.py b/agent/redact.py index 6c713cb4e41..de247ec0ad2 100644 --- a/agent/redact.py +++ b/agent/redact.py @@ -104,6 +104,7 @@ _PREFIX_PATTERNS = [ r"mem0_[A-Za-z0-9]{10,}", # Mem0 Platform API key r"brv_[A-Za-z0-9]{10,}", # ByteRover API key r"xai-[A-Za-z0-9]{30,}", # xAI (Grok) API key + r"ntn_[A-Za-z0-9]{10,}", # Notion internal integration token ] # ENV assignment patterns: KEY=value where KEY contains a secret-like name