hermes-agent/gateway/platforms
Teknium a989a79c0c
fix(gateway): allow native delivery of freshly-produced agent files (#32060)
The gateway's media delivery allowlist required files live inside
`~/.hermes/cache/{documents,images,...}`, which is the wrong shape for
real agent usage. Agents naturally produce artifacts via terminal tools
(`pandoc -o /tmp/report.pdf`, `matplotlib savefig`, etc.) or
write_file into project directories — these never land under the cache.
Result: users got a raw file path in chat instead of an attachment.

This is doubly bad in deployment shapes where the cache directories
aren't writable by the agent at all: Hermes running in Docker with a
read-only mount, or with a Docker/Modal/SSH terminal backend whose
filesystem isn't the gateway host's filesystem.

Layered trust model:

1. Cache-dir allowlist (unchanged) — Hermes-managed roots always trusted.
2. Operator allowlist — `HERMES_MEDIA_ALLOW_DIRS` env var, now also
   surfaced as `gateway.media_delivery_allow_dirs` in config.yaml.
3. Recency-based trust (new, default on) — files whose mtime is within
   `gateway.trust_recent_files_seconds` (default 600s) of "now" are
   trusted even outside the cache/operator allowlist. Old host files
   (`/etc/passwd`, `~/.bashrc`, `~/.ssh/id_rsa`) have mtimes measured
   in days/months, well outside the window — prompt-injection paths
   pointing at pre-existing files are still rejected.
4. Hard denylist — `/etc`, `/proc`, `/sys`, `/dev`, `/root`, `/boot`,
   `/var/{log,lib,run}`, plus `$HOME/.{ssh,aws,gnupg,kube,docker,config,
   azure,gcloud}` and `Library/Keychains`. Denylist blocks delivery
   even when recency would trust the file, in case an attacker
   somehow refreshes a sensitive file's mtime.

Operators who want strict-allowlist behavior set
`gateway.trust_recent_files: false` and the system reverts to
pre-existing behavior.

Tests: 6 new cases in test_platform_base.py cover the recency window,
disabled mode, system-path denylist, and the motivating PDF-in-project
scenario. 3 existing tests (test_platform_base, test_tts_media_routing,
test_send_message_tool) that exercised the strict-allowlist path are
updated to disable recency trust explicitly.

E2E validation: real `validate_media_delivery_path()` accepts fresh
PDFs in /tmp and project dirs, rejects /etc/passwd, ~/.ssh/id_rsa, and
files older than the window; config.yaml `gateway.*` keys bridge
correctly to the env vars the validator reads.
2026-05-25 05:34:31 -07:00
..
qqbot fix(qqbot): authorize approval button interactions by session owner (#30737) 2026-05-24 04:25:12 -07:00
__init__.py perf(gateway): defer QQAdapter and YuanbaoAdapter imports via PEP 562 (#22790) 2026-05-09 13:17:48 -07:00
_http_client_limits.py fix(gateway): tighten httpx keepalive and close whatsapp typing-response leak (#18451) 2026-05-02 02:23:37 -07:00
ADDING_A_PLATFORM.md refactor(plugins): add apply_yaml_config_fn registry hook 2026-05-13 22:20:30 -07:00
api_server.py fix: add cron API provenance logging (#24889) 2026-05-25 01:15:56 -07:00
base.py fix(gateway): allow native delivery of freshly-produced agent files (#32060) 2026-05-25 05:34:31 -07:00
bluebubbles.py fix(debug): redact BlueBubbles webhook secrets 2026-05-24 15:43:48 -07:00
dingtalk.py fix(dingtalk): finalize open streaming cards before disconnect 2026-05-23 20:48:56 -07:00
email.py chore: ruff auto-fix PLR6201 — tuple → set in membership tests (#23937) 2026-05-11 11:13:25 -07:00
feishu.py fix(feishu): validate verification token before reflecting url_verification challenge 2026-05-24 04:51:19 -07:00
feishu_comment.py chore: ruff auto-fix PLR6201 — tuple → set in membership tests (#23937) 2026-05-11 11:13:25 -07:00
feishu_comment_rules.py chore: ruff auto-fix C401, C416, C408, PLR1722 (#23940) 2026-05-11 11:20:58 -07:00
helpers.py fix(gateway): preserve underscores in plain-text identifiers 2026-05-16 23:11:43 -07:00
homeassistant.py chore: ruff auto-fix PLR6201 — tuple → set in membership tests (#23937) 2026-05-11 11:13:25 -07:00
matrix.py fix(matrix,gateway): Matrix E2EE installs full dep set; plugins respect is_connected 2026-05-24 15:16:03 -07:00
msgraph_webhook.py Harden msgraph webhook auth requirements (#30169) 2026-05-24 04:25:20 -07:00
signal.py feat(signal): add require_mention filter for group chats 2026-05-18 23:59:05 -07:00
signal_rate_limit.py feat(gateway/signal): add support for multiple images sending 2026-04-30 04:28:08 -07:00
slack.py fix(gateway): add trust_env=True to aiohttp sessions in SMS, Slack, Teams, Google Chat adapters 2026-05-16 23:11:43 -07:00
sms.py fix(gateway): add trust_env=True to aiohttp sessions in SMS, Slack, Teams, Google Chat adapters 2026-05-16 23:11:43 -07:00
telegram.py fix(telegram): gate send() on send-path health after reconnect storms (#31165) 2026-05-24 15:27:41 -07:00
telegram_network.py fix(telegram): reset sticky fallback IP on connect failure, retry primary DNS 2026-05-18 22:14:45 -07:00
webhook.py fix(webhook): use 403 not 500 for missing-secret rejection 2026-05-24 04:47:45 -07:00
wecom.py fix(wecom): guard flush task against cancel-delivery race to prevent message loss 2026-05-24 01:33:40 -07:00
wecom_callback.py fix(wecom-callback): retry send with fresh token on errcode 40001/42001 2026-05-24 01:30:47 -07:00
wecom_crypto.py feat(gateway): add WeCom callback-mode adapter for self-built apps 2026-04-11 15:22:49 -07:00
weixin.py Fix unsafe gateway media path delivery 2026-05-23 01:40:35 -07:00
whatsapp.py fix(gateway): keep running when platforms fail; add per-platform circuit breaker + /platform (#26600) 2026-05-15 14:32:14 -07:00
yuanbao.py feat(state.db): persist platform_message_id; restore yuanbao exact-id recall 2026-05-20 13:00:57 -07:00
yuanbao_media.py chore: ruff auto-fix PLR6201 — tuple → set in membership tests (#23937) 2026-05-11 11:13:25 -07:00
yuanbao_proto.py chore: ruff auto-fix PLR6201 — tuple → set in membership tests (#23937) 2026-05-11 11:13:25 -07:00
yuanbao_sticker.py yuanbao platform (#16298) 2026-04-26 18:50:49 -07:00