Skip to content

[Feature]: Failed-request dumps write complete Authorization headers (containing API keys) to ~/.hermes/sessions/request_dump_*.json in plaintext — agent/redact.py exists but isn't invoked on this path #19202

@vtbjbb-alt

Description

@vtbjbb-alt

Problem or Use Case

Summary

When a chat request fails (401 / 429 / 500 / etc.), Hermes dumps the entire request—including the Authorization: Bearer sk-... header—to a JSON file at ~/.hermes/sessions/request_dump_<session_id>_<timestamp>.json in plaintext. The project ships agent/redact.py with a redact_sensitive_text function used in 11+ other places, but the dump path bypasses redaction.

This is unfortunate placement for a security-sensitive piece of data:

  • Dumps are exactly what users tar -czf bug-report.tar.gz ~/.hermes/ and email/upload when reporting bugs
  • A single failed session in our environment produced 5 dump files, each containing the full plaintext key
  • grep -r 'sk-proj-' ~/.hermes/ continues to find the old key even after rotating via hermes setup

Evidence

redact_sensitive_text IS used in tool output paths (11 call sites)

tools/terminal_tool.py:2084            terminal output
tools/code_execution_tool.py:895/896   code exec stdout
tools/code_execution_tool.py:1240/1241/1242  code exec stdout/stderr
tools/file_tools.py:553/573/996        file reads
tools/web_tools.py:1241                web tools (uses _PREFIX_RE constant)

redact_sensitive_text IS NOT used in the dump path

# run_agent.py:4247 (origin/main HEAD 6f2dab248; same logic at line 4215 in v2026.4.30-128-g0159f25fd)
dump_file = self.logs_dir / f"request_dump_{self.session_id}_{timestamp}.json"
dump_file.write_text(
    json.dumps(dump_payload, ensure_ascii=False, indent=2, default=str),
    encoding="utf-8",
)

dump_payload is serialized straight to disk via json.dumps with no transformation. Grepping the surrounding 8 lines for redact returns nothing.

Notable: line 4255 introduces an HERMES_DUMP_REQUEST_STDOUT toggle (added in recent upstream commits), so the dump-writing code has been actively touched on main—but redact still wasn't wired up in the same touch. This makes the omission look like an oversight rather than an intentional design choice.

Asymmetry

The architecture is unintuitively backwards: model tool stdout (which is unlikely to contain raw secrets) goes through redact, but failed-request dumps (which always contain Authorization headers and which users actually share with bug reports) do not.

cli.py:593-595 reveals an HERMES_REDACT_SECRETS toggle exists, suggesting the author was aware that redaction is configurable—but even with the toggle ON (default), the dump path still writes plaintext keys (verified empirically: 5 dump files in our deployment, all with Authorization: Bearer sk-proj-...XXXX visible).

Reproduction

  1. Configure any provider with a valid key
  2. Make the chat sub-process produce a failure (e.g. revoke the key mid-session, or temporarily change it to something invalid)
  3. Send a chat message
  4. Inspect ~/.hermes/sessions/request_dump_<session>_<timestamp>.json
  5. Search for Bearer sk- — full key is present in plaintext

Suggested Fixes (in order of preference)

  1. Pipe the dump body through redact_sensitive_text before writing. Consistent with how tool output is handled.
  2. Strip Authorization (and X-Api-Key, Cookie, etc.) from headers before writing. Even if the body is preserved verbatim, headers are easier to enumerate.
  3. Add a HERMES_DUMP_FAILED_REQUESTS toggle defaulting to OFF. Dumps are a power-user debugging feature; default-on is wrong for a tool that handles credentials.

Workaround for operators (current state)

sudo rm -f /srv/hermes-agent/home/.hermes/sessions/request_dump_*.json

A nightly cron job is recommended for any deployment where users might package up ~/.hermes/ for support.

Environment

  • Hermes Agent v0.12.0 (2026.4.30, deployed at commit v2026.4.30-128-g0159f25fd)
  • Bug also confirmed present on origin/main HEAD 6f2dab248 as of 2026-05-03 (62 commits ahead of deployed version; no relevant fix in those commits — agent/redact.py is 0 changes, run_agent.py was modified but redact still not wired up at the dump path)
  • Python 3.11.15
  • OpenAI SDK 2.32.0

Bug analysis, evidence collection, and issue write-up by Claude (Anthropic). Reproduction performed on a production deployment that produced 5 plaintext-key dump files during a single debugging session.


---


### Proposed Solution

-

### Alternatives Considered

_No response_

### Feature Type

New tool

### Scope

None

### Contribution

- [ ] I'd like to implement this myself and submit a PR

### Debug Report (optional)

```shell

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High — major feature broken, no workaroundcomp/agentCore agent loop, run_agent.py, prompt buildersweeper:implemented-on-mainSweeper: behavior already present on current maintype/securitySecurity vulnerability or hardening

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions