Skip to content

fix(gateway): block mcp-tokens/ OAuth tokens from native media delivery - #52633

Closed
Frowtek wants to merge 1 commit into
NousResearch:mainfrom
Frowtek:fix/mcp-tokens-media-delivery
Closed

fix(gateway): block mcp-tokens/ OAuth tokens from native media delivery#52633
Frowtek wants to merge 1 commit into
NousResearch:mainfrom
Frowtek:fix/mcp-tokens-media-delivery

Conversation

@Frowtek

@Frowtek Frowtek commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

The native media-delivery guard in gateway/platforms/base.py can attach
Hermes' MCP OAuth token files (~/.hermes/mcp-tokens/*.json) to a chat reply.

_media_delivery_denied_paths() is documented to mirror the credential
read/write guards in agent/file_safety.py"a credential the agent is
forbidden to write or read must also never be auto-attached to a chat reply."

It enumerates the per-file credential stores and the pairing/ tree, but the
mcp-tokens/ tree was left out (an in-code comment deferred it to a sibling PR
that never landed in-tree). Meanwhile:

  • the read guard (get_read_block_error) blocks the whole mcp-tokens/ tree,
  • the write guard (is_write_denied) blocks the whole mcp-tokens/ tree,
  • .json is in MEDIA_DELIVERY_EXTS, so the file is an eligible attachment.

So validate_media_delivery_path("~/.hermes/mcp-tokens/github.json") returns the
path (deliverable) in both default mode and strict+recency mode. An agent
reply containing MEDIA:~/.hermes/mcp-tokens/github.json (e.g. via prompt
injection) exfiltrates a provider OAuth token JSON to Slack/Telegram/Discord.

Fix: add mcp-tokens to _ROOT_CREDENTIAL_DIRS so the delivery (read/exfil)
side stops trailing the read/write guards. It covers both the active
HERMES_HOME and the shared Hermes root (profile mode) and is symlink-resolved
through the existing containment helper. The misleading "handled by sibling PR"
comment is corrected to state why the entry lives here.

Related Issue

No separate tracked issue — the gap was documented in-code (the

… sibling PR #37222 … kept out of this diff comment that this PR replaces).

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • 🔒 Security fix
  • ✅ Tests (adding or improving test coverage)

Changes Made

  • gateway/platforms/base.py — add "mcp-tokens" to _ROOT_CREDENTIAL_DIRS
    in _media_delivery_denied_paths(); rewrite the stale comment to explain it
    mirrors the read/write guards.
  • tests/gateway/test_platform_base.py — add two regression tests:
    • TestMediaDeliveryDefaultMode::test_denylist_blocks_mcp_tokens_directory_contents
    • TestMediaDeliveryPathValidation::test_recency_trust_denies_mcp_tokens_even_when_fresh
      (guards the in-place-refresh case where the token mtime is always ~now, which
      the recency window would otherwise re-trust).

How to Test

Reproduce (revert the one-line _ROOT_CREDENTIAL_DIRS change first):

from pathlib import Path
import gateway.platforms.base as base
from gateway.platforms.base import BasePlatformAdapter

home = Path("/tmp/hh"); (home / "mcp-tokens").mkdir(parents=True, exist_ok=True)
tok = home / "mcp-tokens" / "github.json"; tok.write_text('{"access_token": "gho_x"}')
base._HERMES_HOME = base._HERMES_ROOT = home
base.MEDIA_DELIVERY_SAFE_ROOTS = ()

print(BasePlatformAdapter.validate_media_delivery_path(str(tok)))
# before fix: prints the token path (deliverable)
# after  fix: prints None

Regression tests:

pytest tests/gateway/test_platform_base.py::TestMediaDeliveryDefaultMode::test_denylist_blocks_mcp_tokens_directory_contents \
       tests/gateway/test_platform_base.py::TestMediaDeliveryPathValidation::test_recency_trust_denies_mcp_tokens_even_when_fresh \
       tests/agent/test_file_safety_credentials.py -q

Result: 21 passed, 1 skipped. Both new tests fail with the fix reverted and
pass with it applied (red→green). The credential read-guard suite
(test_file_safety_credentials.py, incl. the existing mcp-tokens read tests)
stays green — confirming read/write/delivery guards are now consistent.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(gateway):)
  • I searched existing PRs (scanned origin/upstream branches for mcp-tokens/media — no duplicate)
  • My PR contains only changes related to this fix
  • I've run the affected suites and they pass (output above); full hermetic suite runs in CI
  • I've added tests for my changes
  • I've tested on my platform

Documentation & Housekeeping

  • I've updated relevant documentation — N/A (inline comment updated with the fix)
  • I've updated cli-config.yaml.example — N/A (no config keys)
  • I've updated CONTRIBUTING.md / AGENTS.md — N/A (no architecture/workflow change)
  • I've considered cross-platform impact — no OS-specific code; uses the existing symlink-resolved containment helper
  • I've updated tool descriptions/schemas — N/A (no tool behavior change)

The media-delivery denylist in gateway/platforms/base.py mirrored the
credential read/write guards in agent/file_safety.py for the per-file
stores and the pairing/ tree, but omitted mcp-tokens/. The read guard
(get_read_block_error) and the write guard (is_write_denied) both block
the whole mcp-tokens/ tree, yet validate_media_delivery_path() accepted a
file under it in BOTH default and strict+recency modes. An agent reply
containing MEDIA:~/.hermes/mcp-tokens/github.json could therefore attach a
provider OAuth token JSON to a Slack/Telegram/Discord message.

Add mcp-tokens to _ROOT_CREDENTIAL_DIRS so the delivery (read/exfil)
side no longer trails the read/write guards. Covers both the active
HERMES_HOME and the shared Hermes root (profile mode), with
symlink-resolved containment via the existing helper.

Add regression tests for default mode and for strict+recency mode -- the
latter guards the in-place-refresh case where the token mtime is always
~now, which the recency window would otherwise re-trust.
@alt-glitch alt-glitch added type/security Security vulnerability or hardening comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data duplicate This issue or pull request already exists labels Jun 25, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #37222 — same mechanism, same file (gateway/platforms/base.py), same fix: add ~/.hermes/mcp-tokens/ to the media-delivery credential denylist so a MEDIA: prompt-injection can't auto-attach a provider OAuth token JSON. #37222 is the earlier open PR. Also related to the broader whole-HERMES_HOME-tree deny approach in #35939. Maintainer to pick the canonical one.

@Frowtek

Frowtek commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

Dublicate.

@Frowtek Frowtek closed this Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery duplicate This issue or pull request already exists P2 Medium — degraded but workaround exists sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants