fix(security): consolidated security hardening — SSRF, timing attack, tar traversal, credential leakage - #5944
Conversation
… tar traversal, credential leakage Salvaged from PRs #5800 (memosr), #5806 (memosr), #5915 (Ruzzgar), #5928 (Awsh1). Changes: - Use hmac.compare_digest for API key comparison (timing attack prevention) - Apply provider env var blocklist to Docker containers (credential leakage) - Replace tar.extractall() with safe extraction in TerminalBench2 (CVE-2007-4559) - Add SSRF protection via is_safe_url to ALL platform adapters: base.py (cache_image_from_url, cache_audio_from_url), discord, slack, telegram, matrix, mattermost, feishu, wecom (Signal and WhatsApp protected via base.py helpers) - Update tests: mock is_safe_url in Mattermost download tests - Add security tests for tar extraction (traversal, symlinks, safe files)
|
… tar traversal, credential leakage (NousResearch#5944) Salvaged from PRs NousResearch#5800 (memosr), NousResearch#5806 (memosr), NousResearch#5915 (Ruzzgar), NousResearch#5928 (Awsh1). Changes: - Use hmac.compare_digest for API key comparison (timing attack prevention) - Apply provider env var blocklist to Docker containers (credential leakage) - Replace tar.extractall() with safe extraction in TerminalBench2 (CVE-2007-4559) - Add SSRF protection via is_safe_url to ALL platform adapters: base.py (cache_image_from_url, cache_audio_from_url), discord, slack, telegram, matrix, mattermost, feishu, wecom (Signal and WhatsApp protected via base.py helpers) - Update tests: mock is_safe_url in Mattermost download tests - Add security tests for tar extraction (traversal, symlinks, safe files)
Discord attachment uploads silently break for users behind DNS-rewriting proxies (Clash/Mihomo fake-ip mode). The proxy resolves cdn.discordapp.com to a fake 198.18.x.x address from the IETF benchmark range (RFC 6890), so the SSRF guard added in NousResearch#5944 rejects every attachment as "unsafe". Symptom in the gateway log: [Discord] Failed to cache image attachment: Blocked unsafe URL (SSRF protection): https://cdn.discordapp.com/... Root cause: cache_image_from_url / cache_audio_from_url validate the resolved IP, but Discord attachment URLs come straight from discord.py, which has already authenticated with the platform. The IP-based check is both unreliable (DNS rewriting) and unnecessary (URL is already trusted). Fix: add a `trusted_source` parameter that opts out of the SSRF check. Discord's inbound attachment handler passes trusted_source=True. The default remains False so agent-supplied URLs (e.g. send_image with a user-provided link) keep the safety check. Tests: - 4 new tests in TestTrustedSourceBypass covering both helpers - Add an autouse _mock_safe_url fixture to TestCacheImageFromUrl / TestCacheAudioFromUrl so the existing retry tests do not depend on the developer local DNS resolver (which previously failed under fake-ip) Verified: 37/37 media-cache tests pass, no other regressions in tests/gateway/.
… tar traversal, credential leakage (NousResearch#5944) Salvaged from PRs NousResearch#5800 (memosr), NousResearch#5806 (memosr), NousResearch#5915 (Ruzzgar), NousResearch#5928 (Awsh1). Changes: - Use hmac.compare_digest for API key comparison (timing attack prevention) - Apply provider env var blocklist to Docker containers (credential leakage) - Replace tar.extractall() with safe extraction in TerminalBench2 (CVE-2007-4559) - Add SSRF protection via is_safe_url to ALL platform adapters: base.py (cache_image_from_url, cache_audio_from_url), discord, slack, telegram, matrix, mattermost, feishu, wecom (Signal and WhatsApp protected via base.py helpers) - Update tests: mock is_safe_url in Mattermost download tests - Add security tests for tar extraction (traversal, symlinks, safe files)
Excellence Contribution — Consolidated Security HardeningWhat it does ① Timing attack prevention — API server auth replaced ② Docker credential leakage — ③ Tar path traversal (CVE-2007-4559 mitigation) — ④ SSRF protection (extended to all 9 adapters) — Media download functions on Discord, Slack, Telegram, Matrix, Mattermost, Feishu, WeCom, Signal, and WhatsApp all accepted arbitrary URLs. Protected adapters: Implications
|
… tar traversal, credential leakage (NousResearch#5944) Salvaged from PRs NousResearch#5800 (memosr), NousResearch#5806 (memosr), NousResearch#5915 (Ruzzgar), NousResearch#5928 (Awsh1). Changes: - Use hmac.compare_digest for API key comparison (timing attack prevention) - Apply provider env var blocklist to Docker containers (credential leakage) - Replace tar.extractall() with safe extraction in TerminalBench2 (CVE-2007-4559) - Add SSRF protection via is_safe_url to ALL platform adapters: base.py (cache_image_from_url, cache_audio_from_url), discord, slack, telegram, matrix, mattermost, feishu, wecom (Signal and WhatsApp protected via base.py helpers) - Update tests: mock is_safe_url in Mattermost download tests - Add security tests for tar extraction (traversal, symlinks, safe files)
… tar traversal, credential leakage (NousResearch#5944) Salvaged from PRs NousResearch#5800 (memosr), NousResearch#5806 (memosr), NousResearch#5915 (Ruzzgar), NousResearch#5928 (Awsh1). Changes: - Use hmac.compare_digest for API key comparison (timing attack prevention) - Apply provider env var blocklist to Docker containers (credential leakage) - Replace tar.extractall() with safe extraction in TerminalBench2 (CVE-2007-4559) - Add SSRF protection via is_safe_url to ALL platform adapters: base.py (cache_image_from_url, cache_audio_from_url), discord, slack, telegram, matrix, mattermost, feishu, wecom (Signal and WhatsApp protected via base.py helpers) - Update tests: mock is_safe_url in Mattermost download tests - Add security tests for tar extraction (traversal, symlinks, safe files)
… tar traversal, credential leakage (NousResearch#5944) Salvaged from PRs NousResearch#5800 (memosr), NousResearch#5806 (memosr), NousResearch#5915 (Ruzzgar), NousResearch#5928 (Awsh1). Changes: - Use hmac.compare_digest for API key comparison (timing attack prevention) - Apply provider env var blocklist to Docker containers (credential leakage) - Replace tar.extractall() with safe extraction in TerminalBench2 (CVE-2007-4559) - Add SSRF protection via is_safe_url to ALL platform adapters: base.py (cache_image_from_url, cache_audio_from_url), discord, slack, telegram, matrix, mattermost, feishu, wecom (Signal and WhatsApp protected via base.py helpers) - Update tests: mock is_safe_url in Mattermost download tests - Add security tests for tar extraction (traversal, symlinks, safe files)
… tar traversal, credential leakage (NousResearch#5944) Salvaged from PRs NousResearch#5800 (memosr), NousResearch#5806 (memosr), NousResearch#5915 (Ruzzgar), NousResearch#5928 (Awsh1). Changes: - Use hmac.compare_digest for API key comparison (timing attack prevention) - Apply provider env var blocklist to Docker containers (credential leakage) - Replace tar.extractall() with safe extraction in TerminalBench2 (CVE-2007-4559) - Add SSRF protection via is_safe_url to ALL platform adapters: base.py (cache_image_from_url, cache_audio_from_url), discord, slack, telegram, matrix, mattermost, feishu, wecom (Signal and WhatsApp protected via base.py helpers) - Update tests: mock is_safe_url in Mattermost download tests - Add security tests for tar extraction (traversal, symlinks, safe files)
Summary
Salvaged and extended fixes from 4 security PRs into a single consolidated PR.
What this PR does
① Timing attack prevention (from #5800 by @memosr)
token == self._api_keywithhmac.compare_digest()in API server auth② Docker credential leakage (from #5806 by @memosr)
_HERMES_PROVIDER_ENV_BLOCKLISTto Docker env forwarding③ Tar path traversal (from #5928 by @Awsh1)
tar.extractall()with_safe_extract_tar()in TerminalBench2../), absolute paths, symlinks, non-file members④ SSRF protection (extended from #5915 by @Ruzzgar)
is_safe_url()added to both shared helpers in base.py AND each adapterProtected adapters:
cache_image_from_url(),cache_audio_from_url()send_image()before aiohttp downloadsend_image()before httpx downloadsend_image()before URL pass-through + httpx fallbacksend_image()before aiohttp/httpx download_send_url_as_file()before aiohttp download_download_remote_document()before httpx download_download_remote_bytes()before httpx streamcache_image_from_url()in base.pycache_image_from_url()in base.pyFiles changed (14 files, +284/-11)
gateway/platforms/api_server.py— hmac.compare_digesttools/environments/docker.py— env var blocklistenvironments/benchmarks/terminalbench_2/terminalbench2_env.py— safe tar extractiongateway/platforms/base.py— SSRF in cache helpersgateway/platforms/{discord,slack,telegram,matrix,mattermost,feishu,wecom}.py— SSRF in adapterstests/environments/benchmarks/test_terminalbench2_env_security.py— new security teststests/gateway/test_mattermost.py,tests/gateway/test_media_download_retry.py— mock is_safe_urlTest results
Closes #5800, #5806, #5915, #5928.