Skip to content

fix(agent): stop redacting tool-call args in history; fix auth-header quote-eating (#43083) - #54136

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-cffaf927
Jun 28, 2026
Merged

fix(agent): stop redacting tool-call args in history; fix auth-header quote-eating (#43083)#54136
teknium1 merged 2 commits into
mainfrom
hermes/hermes-cffaf927

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

Credential-bearing tool calls now keep working across turns, and masking an Authorization: header no longer eats the closing quote.

Two related redaction bugs reported in #43083:

  1. History poisoning. build_assistant_message() masked tool-call arguments in-memory. That dict feeds both the conversation history replayed to the model every turn and state.db, which is itself replayed verbatim on session resume (get_messages_as_conversation). So a model that ran PGPASSWORD='real' psql ... read back its own PGPASSWORD='***' call on the next turn and copied the placeholder — every credential-dependent command broke on the second call. The masking also gave no real protection: the same secret still leaks through tool output (file contents, command output, diffs, the compaction block), which this pass never touched.
  2. Quote-eating regex (@shady2k). _AUTH_HEADER_RE's greedy \S+ credential class pulled in a closing quote when the token sat flush against it ("Authorization: Bearer sk-..."), turning value corruption into syntax corruption — an unterminated quote → shell EOF / Python SyntaxError.

Changes

  • agent/chat_completion_helpers.py: stop redacting tool-call arguments in build_assistant_message. Keeping secrets out of the replayable store is a separate tokenization/vault concern — it cannot be done by masking the replayed history without breaking replay. Storage-time redaction elsewhere is unchanged and still governed by security.redact_secrets.
  • agent/redact.py: _AUTH_HEADER_RE credential class now excludes "/' ([^\s"']+). Real credentials never contain quotes, so the mask stops at the quote boundary.
  • tests/agent/test_redact.py: +2 regression tests — token flush against " and ' keeps both quotes balanced.
  • tests/agent/test_tool_call_arg_no_redaction.py: +2 tests — tool-call args (PGPASSWORD, Bearer token) are preserved verbatim even with redaction forced on.

Validation

Before After
PGPASSWORD='real' psql on turn 2 *** copied → fails real value preserved → works
"Authorization: Bearer sk-..." masked Bearer *** (closing " eaten) Bearer ***" (quote survives)
Tests (test_redact + new) 104 pass

Closes #43083.

Infographic

infographic

teknium1 added 2 commits June 28, 2026 02:25
… quote-eating

Two related redaction bugs from #43083:

1. build_assistant_message redacted tool-call arguments in-memory. That dict
   feeds both the replayed conversation history and state.db (which is itself
   replayed verbatim on session resume), so the model read back its own
   PGPASSWORD='***' psql call and copied the placeholder, breaking every
   credential-dependent command on the second turn. The masking gave no real
   protection either — the same secret still leaks through tool OUTPUT. Remove
   it. Keeping secrets out of the replayable store is a separate
   tokenization/vault concern (security.redact_secrets still governs
   storage-time redaction elsewhere).

2. _AUTH_HEADER_RE's greedy \S+ credential class ate a closing quote when the
   token sat flush against it (Authorization: Bearer sk-.."), turning value
   corruption into syntax corruption (unterminated quote -> shell EOF /
   SyntaxError). Exclude " and ' from the token class; real credentials never
   contain them.

Closes #43083.
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-cffaf927 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 11622 on HEAD, 11622 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 6103 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint area/auth Authentication, OAuth, credential pools sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data P2 Medium — degraded but workaround exists labels Jun 28, 2026
@teknium1
teknium1 merged commit 822b71c into main Jun 28, 2026
28 of 30 checks passed
@teknium1
teknium1 deleted the hermes/hermes-cffaf927 branch June 28, 2026 09:44
WolframRavenwolf added a commit to WolframRavenwolf/hermes-agent that referenced this pull request Jul 14, 2026
Keep canonical tool-call arguments byte-exact so replay and resume cannot reuse redaction placeholders. Narrow operational metadata false positives, preserve quote/backslash syntax, and align Anthropic interleaved replay with the raw canonical argument contract. Adapted from merged upstream PRs NousResearch#54061/NousResearch#54136 and a hardened subset of open PR NousResearch#47348.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Passwords get replaced by *** but model reads back its own conversation history and fails on second tool call.

2 participants