Skip to content

fix(redact): stop masking prose words that embed a secret keyword (port of ironclaw#6129) - #67776

Merged
teknium1 merged 1 commit into
mainfrom
ironclaw-port/redact-keyword-word-boundary
Jul 27, 2026
Merged

fix(redact): stop masking prose words that embed a secret keyword (port of ironclaw#6129)#67776
teknium1 merged 1 commit into
mainfrom
ironclaw-port/redact-keyword-word-boundary

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

Prose/document words that merely embed a secret keyword — Secretary: (secret), tokenizer: (token), BibTeX author= (auth) — no longer get value-masked by the lowercase/dotted/YAML config-key redaction passes. Real credential key shapes (separators, camelCase, acronyms, plurals, common concatenated compounds, all-caps env style) redact exactly as before.

Port of nearai/ironclaw#6129, where the same bug class (sensitive markers matched as bare substrings) scrubbed tool results containing "Secretary of the Treasury" as secret on transcript replay, evicting legitimate content and sending the model into a re-fetch loop.

Root cause in hermes: _CFG_DOTTED_RE / _CFG_ANCHORED_RE / _YAML_ASSIGN_RE key classes allow arbitrary alphanumeric affixes around the keyword ([A-Za-z0-9_.\-]*secret[A-Za-z0-9_.\-]*), so Secretary, Undersecretary, secretariat, tokenizer, authored, credentialing all matched as "keys". Confirmed live on main:

Secretary: JanetYellen…   →  Secretary: JanetY...e123   (value mangled)
tokenizer: cl100k_base…   →  tokenizer: cl100k...ame    (HF model-card metadata)
author=Smith2020…         →  author=***                 (BibTeX)

These passes run on model- and user-visible surfaces: browser snapshots (force=True), kanban summaries, TUI verbose text, CLI-echoed command output, and log lines. (File reads and ordinary terminal output are unaffected — they use code_file=True, which skips these passes.)

Changes

  • agent/redact.py: _key_has_secret_keyword() post-match validator wired into the _redact_env / _redact_yaml callbacks. A keyword occurrence counts only at a word boundary within the key:
    • key edges, non-letters (_ - . digits) — client_secret, db.password, oauth2_token
    • camelCase transitions — clientSecret, secretKey, APIToken (acronym-run rule)
    • trailing plural ssecrets:, tokens:
    • explicit concatenated compounds keep matching — authtoken (ngrok), authkey (tailscale), secretkey (minio), accesstoken, apikey
    • ALL-CAPS keys keep legacy embedded matching (MYTOKEN=…) — all-caps is almost never prose, same rationale as _ENV_ASSIGN_RE (which is untouched)
  • tests/agent/test_redact.py: new TestKeywordWordBoundary class — 8 preserved-prose cases + 7 still-masked shape groups.

This is the same discipline redact.py already applies elsewhere: the body/query key sets are exact-match "NOT substring" (ported from ironclaw#2529), and bare auth was already excluded from the YAML key set so author: wouldn't match — this closes the remaining affix hole.

Adaptation notes

IronClaw's fix is a pure alnum-boundary check on lowercased text in their transcript-replay scrubber. Hermes' equivalent surface is the config-key redaction patterns, and hermes keys are not pre-lowercased, so the port adds camelCase/acronym boundary awareness (their lowercased haystack can't distinguish clientSecret from secretary) plus plural and compound handling to avoid false negatives on real key shapes IronClaw's marker list doesn't have.

Validation

Case Before After
Secretary: JanetYellen… value masked preserved
tokenizer: cl100k_base… value masked preserved
author=Smith2020… (BibTeX) value masked preserved
press.secretary=… value masked preserved
client_secret: / clientSecret: / APIToken: masked masked
authtoken: / authkey= / secretkey: / secrets: masked masked
MYTOKEN=… (all-caps) masked masked
  • E2E probe (33 cases, real import, force=True): all pass
  • tests/agent/test_redact.py: 163 passed (148 existing + 15 new)
  • Sibling suites (test_terminal_output_transform_hook, test_kanban_redaction, test_browser_type_redaction, test_approval_prompt_redaction, test_redact_config_bridge, test_browser_secret_exfil, test_debug, test_trace_upload, test_signal, test_tui_gateway_server, +4 more): 1,092 passed

Infographic

word-boundary-redaction

…cretary, tokenizer, author=)

Port from nearai/ironclaw#6129: their sensitive-marker scrubber matched
markers as bare substrings, so tool results containing 'Secretary of the
Treasury' were scrubbed as 'secret' on replay, evicting legitimate content
and forcing the model into a re-fetch loop. Hermes' lowercase/dotted/YAML
config-key redaction patterns (_CFG_DOTTED_RE, _CFG_ANCHORED_RE,
_YAML_ASSIGN_RE) had the same false-positive class: their key classes allow
arbitrary alphanumeric affixes around the keyword, so ordinary document
text like 'Secretary: J.Smith', 'tokenizer: cl100k_base' (HF model cards),
and BibTeX 'author=Smith' got value-masked on the surfaces that run these
passes (browser snapshots, log lines, kanban summaries, CLI-echoed output).

Fix: post-match word-boundary validation of the keyword occurrence inside
the matched key. Boundaries: key edges, non-letters (_ - . digits),
camelCase transitions (clientSecret, secretKey, APIToken), plural 's'
(secrets:, tokens:). Concatenated real-world compounds keep matching via
explicit alternatives (authtoken, authkey, secretkey, accesstoken). ALL-CAPS
keys keep legacy embedded matching (MYTOKEN=...) — all-caps is almost never
prose, same rationale as _ENV_ASSIGN_RE. Same discipline the file already
applies to exact-match body/query keys (ported from ironclaw#2529) and the
deliberate 'auth' exclusion that keeps 'author:' from matching.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint labels Jul 20, 2026
@teknium1
teknium1 merged commit b41eee4 into main Jul 27, 2026
33 checks passed
@teknium1
teknium1 deleted the ironclaw-port/redact-keyword-word-boundary branch July 27, 2026 03:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants