hermes - 💡(How to fix) Fix Security: outbound chat messages bypass redact_sensitive_text in v0.13.0 (gateway/platforms/*.py)

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

The redact_sensitive_text() function in agent/redact.py ships correct regex patterns for API keys, tokens, and credentials (sk-*, ghp_*, github_pat_*, Telegram bot<digits>:<token>, AWS, JWTs, DB connection strings, etc.). It is correctly applied to log records via RedactingFormatter and to tool output before it enters the LLM context. However, it is NOT applied to LLM-generated responses being delivered to messaging platforms (Telegram verified directly; Discord and Slack likely affected by the same code path).

The gateway startup banner logs:

INFO gateway.run: Secret redaction: ENABLED (tool output, logs, and chat responses are scrubbed before delivery)

This is misleading: "chat responses are scrubbed before delivery" is true only of log records of chat responses (via the RedactingFormatter on log handlers), not of the actual delivered message bytes.

Sibling to #17691 (HERMES_REDACT_SECRETS default-OFF). That issue addressed the default flag; this issue is about the function's CALL SITE coverage on the gateway outbound path.

Root Cause

The redact_sensitive_text() function in agent/redact.py ships correct regex patterns for API keys, tokens, and credentials (sk-*, ghp_*, github_pat_*, Telegram bot<digits>:<token>, AWS, JWTs, DB connection strings, etc.). It is correctly applied to log records via RedactingFormatter and to tool output before it enters the LLM context. However, it is NOT applied to LLM-generated responses being delivered to messaging platforms (Telegram verified directly; Discord and Slack likely affected by the same code path).

The gateway startup banner logs:

INFO gateway.run: Secret redaction: ENABLED (tool output, logs, and chat responses are scrubbed before delivery)

This is misleading: "chat responses are scrubbed before delivery" is true only of log records of chat responses (via the RedactingFormatter on log handlers), not of the actual delivered message bytes.

Sibling to #17691 (HERMES_REDACT_SECRETS default-OFF). That issue addressed the default flag; this issue is about the function's CALL SITE coverage on the gateway outbound path.

Fix Action

Workaround

For now, deployments with this gap can:

  1. Restrict TELEGRAM_ALLOWED_USERS and TELEGRAM_HOME_CHANNEL to a single trusted operator (audience-of-one mitigation, leak still happens but only to the operator).
  2. Audit every enabled skill for tool-output paths that could surface a secret to LLM context.
  3. Patch locally by overriding BasePlatform.send_message (or equivalent) to wrap text through redact_sensitive_text.

Happy to send a PR if there's interest. Pointers on which file should own the call site (base, per-platform, or a separate middleware layer) would help shape the diff.

Code Example

INFO gateway.run: Secret redaction: ENABLED (tool output, logs, and chat responses are scrubbed before delivery)

---

security:
  redact_secrets: true

unauthorized_dm_behavior: ignore

platform_toolsets:
  telegram: [messaging]
  # others: []

platforms:
  telegram:
    extra:
      disable_link_previews: true

---

TELEGRAM_BOT_TOKEN=<your-bot-token>
TELEGRAM_ALLOWED_USERS=<your-user-id>
TELEGRAM_HOME_CHANNEL=<your-user-id>

---

from agent.redact import redact_sensitive_text

# ... existing formatting (markdown escape, table wrap, etc.)
text = redact_sensitive_text(text)
await self._platform_send(chat_id, text)
RAW_BUFFERClick to expand / collapse

Summary

The redact_sensitive_text() function in agent/redact.py ships correct regex patterns for API keys, tokens, and credentials (sk-*, ghp_*, github_pat_*, Telegram bot<digits>:<token>, AWS, JWTs, DB connection strings, etc.). It is correctly applied to log records via RedactingFormatter and to tool output before it enters the LLM context. However, it is NOT applied to LLM-generated responses being delivered to messaging platforms (Telegram verified directly; Discord and Slack likely affected by the same code path).

The gateway startup banner logs:

INFO gateway.run: Secret redaction: ENABLED (tool output, logs, and chat responses are scrubbed before delivery)

This is misleading: "chat responses are scrubbed before delivery" is true only of log records of chat responses (via the RedactingFormatter on log handlers), not of the actual delivered message bytes.

Sibling to #17691 (HERMES_REDACT_SECRETS default-OFF). That issue addressed the default flag; this issue is about the function's CALL SITE coverage on the gateway outbound path.

Reproduction

Hermes Agent v0.13.0 (v2026.5.7), Docker container.

config.yaml:

security:
  redact_secrets: true

unauthorized_dm_behavior: ignore

platform_toolsets:
  telegram: [messaging]
  # others: []

platforms:
  telegram:
    extra:
      disable_link_previews: true

/path/to/secrets.env:

TELEGRAM_BOT_TOKEN=<your-bot-token>
TELEGRAM_ALLOWED_USERS=<your-user-id>
TELEGRAM_HOME_CHANNEL=<your-user-id>

Start gateway: hermes gateway run. Gateway connects to Telegram and logs Secret redaction: ENABLED (tool output, logs, and chat responses are scrubbed before delivery).

From your allowlisted Telegram account, DM the bot a block asking it to echo back five synthetic token-shape lines, one per pattern under test:

  • LINE1: a sk- + 20 random hex chars (OpenAI/OpenRouter pattern)
  • LINE2: a github + _pat_ + 82 random [A-Za-z0-9_] chars (GitHub fine-grained PAT)
  • LINE3: a gh + p_ + 36 random [A-Za-z0-9] chars (GitHub classic PAT)
  • LINE4: a bot + 10 digits + : + 40 random [-A-Za-z0-9_] chars (Telegram bot token)
  • LINE5: a sk- + or-v1- + 73 random [A-Za-z0-9] chars (OpenRouter v1 key)

(Prefixes intentionally broken in this issue body so GitHub secret scanning doesn't reject the post; reconstruct them exactly when running the reproduction. Use synthetic placeholder bodies, never real tokens.)

Expected

Bot reply contains each LINE wrapper but token bodies are masked (e.g. sk-tes...cdef, or ***).

Actual

Bot reply contains all 5 token shapes verbatim, unredacted. Gateway log confirms Sending response (414 chars) matching the unredacted payload size.

The session JSONL transcript at /opt/data/sessions/<session>.jsonl records the assistant content with the tokens still verbatim, i.e. the LLM emitted them (which is fine; LLMs echo what users ask), but the gateway delivered them as-is to Telegram with no scrubbing pass.

Code path

Inspected at upstream tag v2026.5.7:

  • gateway/platforms/base.py: no redact_sensitive_text call, no from agent.redact import.
  • gateway/platforms/telegram.py: no redact_sensitive_text call before bot.send_message / application.bot.send_message. Text transformation in this path is limited to _escape_mdv2, _strip_mdv2, and _wrap_markdown_tables (formatting, not redaction).

By contrast, RedactingFormatter in agent/redact.py correctly wraps log handler output, so the same content IS scrubbed when written to /opt/data/logs/*.log.

Suggested fix

In gateway/platforms/base.py (or wherever the canonical outbound delivery happens), wrap the outbound text through agent.redact.redact_sensitive_text(text) after Markdown formatting and before the platform-specific send call:

from agent.redact import redact_sensitive_text

# ... existing formatting (markdown escape, table wrap, etc.)
text = redact_sensitive_text(text)
await self._platform_send(chat_id, text)

Or apply per-platform if scoping is needed (some operators may want raw output on internal Slack DMs, etc.). Either way, the call site should match what the startup banner already claims.

Alternatively, if the existing call path is intentional and the banner is incorrect, the fix is to update the banner text to clarify that only logs + tool output are scrubbed, and add a separate config flag (e.g. security.redact_outbound_chat: true) to opt into outbound scrubbing for operators who want it.

Impact

For deployments using Telegram/Discord/Slack as the primary delivery channel for AI agent output (i.e. the documented v0.13 use case), any token-shape string that reaches the LLM's response, whether echoed back from a user prompt, accidentally surfaced from a skill that read a credential file, or hallucinated as part of LLM output, is delivered to the user verbatim. Gateway operators who rely on the Secret redaction: ENABLED log line will infer a security posture that doesn't match reality.

For our deployment we've shipped with a documented gap and audience-of-one mitigation; we'd prefer a defence-in-depth fix upstream.

Versions

  • nousresearch/hermes-agent:v2026.5.7 (= v0.13.0 "Tenacity Release") multi-arch image digest sha256:9462082533e603fc6ba4259c51d7722dcd8cbb0f348488ceedc35d06fa8f9324
  • Docker, Linux amd64
  • python-telegram-bot via upstream deps

Workaround

For now, deployments with this gap can:

  1. Restrict TELEGRAM_ALLOWED_USERS and TELEGRAM_HOME_CHANNEL to a single trusted operator (audience-of-one mitigation, leak still happens but only to the operator).
  2. Audit every enabled skill for tool-output paths that could surface a secret to LLM context.
  3. Patch locally by overriding BasePlatform.send_message (or equivalent) to wrap text through redact_sensitive_text.

Happy to send a PR if there's interest. Pointers on which file should own the call site (base, per-platform, or a separate middleware layer) would help shape the diff.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix Security: outbound chat messages bypass redact_sensitive_text in v0.13.0 (gateway/platforms/*.py)