Skip to content

fix(gateway): bypass SSRF check for Discord SDK-supplied attachment URLs - #6512

Closed
rivercrab26 wants to merge 1 commit into
NousResearch:mainfrom
rivercrab26:fix/discord-attachment-ssrf-bypass
Closed

fix(gateway): bypass SSRF check for Discord SDK-supplied attachment URLs#6512
rivercrab26 wants to merge 1 commit into
NousResearch:mainfrom
rivercrab26:fix/discord-attachment-ssrf-bypass

Conversation

@rivercrab26

Copy link
Copy Markdown
Contributor

Summary

Fixes #6511.

Discord image/audio attachments fail to cache under DNS-rewriting proxies (Clash/Mihomo fake-ip mode) because the SSRF guard added in #5944 rejects the resolved fake IP (198.18.x.x) as private. The agent then cannot see images uploaded by users on Discord. See the linked issue for the full root-cause analysis.

Approach

Add an opt-in trusted_source: bool = False parameter to cache_image_from_url and cache_audio_from_url in gateway/platforms/base.py. When True, the IP-based SSRF check is skipped.

The Discord adapter passes trusted_source=True when downloading inbound discord.Attachment URLs, because:

  1. The URL is supplied directly by discord.py and is already authenticated by Discord — it is not user/agent controllable in the way a free-form URL is.
  2. IP-based reachability checks are unreliable when the user runs a proxy that rewrites DNS responses (this is the normal configuration for many proxy stacks).
  3. A third-party CDN may legitimately resolve to any address; defending against the content of cdn.discordapp.com is the wrong layer.

The default remains False, so all other call sites — including agent-supplied send_image URLs — keep the existing safety check unchanged.

Files

File Change
gateway/platforms/base.py Add trusted_source parameter to both helpers
gateway/platforms/discord.py Pass trusted_source=True for inbound attachment downloads (image + audio)
tests/gateway/test_media_download_retry.py New TestTrustedSourceBypass class (4 tests) + autouse _mock_safe_url fixture so existing retry tests do not depend on the developer's local DNS

Tests

tests/gateway/test_media_download_retry.py ............................... [100%]
37 passed in 0.95s

Coverage:

  • test_image_blocked_by_default_when_unsafe — default behavior unchanged
  • test_image_trusted_source_bypasses_ssrf — opt-in skips the check
  • test_audio_blocked_by_default_when_unsafe — same for audio helper
  • test_audio_trusted_source_bypasses_ssrf — same for audio helper
  • The fixture also asserts is_safe_url is not called when trusted_source=True, so future regressions are caught.

Why the existing tests needed a fixture

While debugging I noticed the existing TestCacheImageFromUrl / TestCacheAudioFromUrl tests fail on any developer machine that uses fake-ip DNS — they call out to a real is_safe_url("http://example.com/..."), which under fake-ip resolves to a private 198.18.x.x address and trips the very guard the tests are not meant to exercise. I added a small autouse fixture that pins is_safe_url to True for these classes. This is a pure test isolation fix and is independent of the production change.

Out of scope

Risk

Minimal. The trusted_source flag defaults to False, so all existing call sites behave identically. Only the Discord inbound-attachment path is opted in, and for that path the URL is already authenticated by the Discord API.

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/.
@atop0914

Copy link
Copy Markdown

Same issue here — voice messages and images both fail to process. The SSRF check blocks cdn.discordapp.com because it resolves to a 198.18.x.x address in my environment. Hope this gets merged soon!

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the careful analysis and the reproduction detail — the trusted_source design is clean and the PR is well-structured.

This is an automated hermes-sweeper review.

The root cause you identified (fake-ip DNS resolving cdn.discordapp.com to 198.18.x.x, tripping is_safe_url()) was fixed on main by a different approach before this PR could be merged:

The @atop0914 report and the original issue should be resolved after updating to a version that includes #11568. If you're still seeing failures after that update, please re-open with updated reproduction steps.

@teknium1 teknium1 closed this Apr 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Discord image/audio attachments fail under DNS-rewriting proxies (false-positive SSRF block from #5944)

3 participants