hermes - 💡(How to fix) Fix Bug: send_message tool bypasses validate_media_delivery_path security check

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…

Root Cause

In tools/send_message_tool.py, the flow is:

# Line 253 — extract_media does parse MEDIA: tags ✅
media_files, cleaned_message = BasePlatformAdapter.extract_media(message)

# Line 254 — filter_media_delivery_paths is called
media_files = BasePlatformAdapter.filter_media_delivery_paths(media_files)

# Line 304-310 — passed to _send_to_platform → _send_telegram
# _send_telegram opens and sends the file directly via bot.send_document()
# WITHOUT calling validate_media_delivery_path()

The gateway's response pipeline (base.py:3642) calls filter_media_delivery_pathsvalidate_media_delivery_path which checks the allowlist. But _send_telegram() (line 946-977) only checks os.path.exists() — it never validates against allowed roots.

Fix Action

Fix / Workaround

  • Single-user (local): Low risk — the user controls their own files
  • Multi-user / public gateway: Medium risk — a prompt injection attack could instruct the agent to use send_message with MEDIA: tags pointing to sensitive files (e.g., ~/.ssh/id_rsa, ~/.aws/credentials) and exfiltrate them to a chat controlled by the attacker
  • Severity: P2 — security bypass with workaround (don't run public gateways)

Code Example

# Line 253 — extract_media does parse MEDIA: tags ✅
media_files, cleaned_message = BasePlatformAdapter.extract_media(message)

# Line 254 — filter_media_delivery_paths is called
media_files = BasePlatformAdapter.filter_media_delivery_paths(media_files)

# Line 304-310 — passed to _send_to_platform → _send_telegram
# _send_telegram opens and sends the file directly via bot.send_document()
# WITHOUT calling validate_media_delivery_path()

---

# In _send_telegram(), around line 946:
for media_path, is_voice in media_files:
    if not os.path.exists(media_path):
        warning = f"Media file not found, skipping: {media_path}"
        logger.warning(warning)
        warnings.append(warning)
        continue

    # ADD: validate against safe roots
    from gateway.platforms.base import validate_media_delivery_path
    if not validate_media_delivery_path(media_path):
        warning = f"Skipping unsafe media path outside allowed roots: {media_path}"
        logger.warning(warning)
        warnings.append(warning)
        continue

    ext = os.path.splitext(media_path)[1].lower()
    # ... rest of send logic
RAW_BUFFERClick to expand / collapse

Bug Description

The send_message tool's _send_telegram() function (line 813 of tools/send_message_tool.py) delivers media files directly via bot.send_document() without calling validate_media_delivery_path(). This bypasses the MEDIA_DELIVERY_SAFE_ROOTS allowlist that the gateway's response delivery pipeline enforces.

Impact

  • Single-user (local): Low risk — the user controls their own files
  • Multi-user / public gateway: Medium risk — a prompt injection attack could instruct the agent to use send_message with MEDIA: tags pointing to sensitive files (e.g., ~/.ssh/id_rsa, ~/.aws/credentials) and exfiltrate them to a chat controlled by the attacker
  • Severity: P2 — security bypass with workaround (don't run public gateways)

Reproduction

  1. Have the agent call send_message(message="MEDIA:/etc/hosts", target="telegram")
  2. The file is delivered as a document attachment despite /etc/ not being in MEDIA_DELIVERY_SAFE_ROOTS
  3. Compare with the gateway response pipeline which correctly logs: "Skipping unsafe MEDIA directive path outside allowed roots"

Root Cause

In tools/send_message_tool.py, the flow is:

# Line 253 — extract_media does parse MEDIA: tags ✅
media_files, cleaned_message = BasePlatformAdapter.extract_media(message)

# Line 254 — filter_media_delivery_paths is called
media_files = BasePlatformAdapter.filter_media_delivery_paths(media_files)

# Line 304-310 — passed to _send_to_platform → _send_telegram
# _send_telegram opens and sends the file directly via bot.send_document()
# WITHOUT calling validate_media_delivery_path()

The gateway's response pipeline (base.py:3642) calls filter_media_delivery_pathsvalidate_media_delivery_path which checks the allowlist. But _send_telegram() (line 946-977) only checks os.path.exists() — it never validates against allowed roots.

Suggested Fix

Add a validate_media_delivery_path() check in _send_telegram() before opening the file:

# In _send_telegram(), around line 946:
for media_path, is_voice in media_files:
    if not os.path.exists(media_path):
        warning = f"Media file not found, skipping: {media_path}"
        logger.warning(warning)
        warnings.append(warning)
        continue

    # ADD: validate against safe roots
    from gateway.platforms.base import validate_media_delivery_path
    if not validate_media_delivery_path(media_path):
        warning = f"Skipping unsafe media path outside allowed roots: {media_path}"
        logger.warning(warning)
        warnings.append(warning)
        continue

    ext = os.path.splitext(media_path)[1].lower()
    # ... rest of send logic

Additional Finding

The MEDIA: regex in base.py:2461 does not support .md files (extension not in the pattern). Consider adding md to the extension list.

Environment

  • Hermes v0.15.0
  • macOS 26.5
  • Platform: Telegram

Related

  • #29523 — the PR that introduced validate_media_delivery_path and the safe-roots allowlist

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