fix(discord,slack): add SSRF policy for media downloads in proxy environments#25475
Conversation
…ronments Discord and Slack media downloads (attachments, stickers, forwarded images) call fetchRemoteMedia without any ssrfPolicy. When running behind a local transparent proxy (Clash, mihomo, Shadowrocket) in fake-ip mode, DNS returns virtual IPs in the 198.18.0.0/15 range, which the SSRF guard blocks. Add per-channel SSRF policy constants—matching the pattern already applied to Telegram on main—that allowlist known CDN hostnames and set allowRfc2544BenchmarkRange: true. Refs openclaw#25355, openclaw#25322 Co-authored-by: Cursor <cursoragent@cursor.com>
4a035d3 to
8527711
Compare
🔒 Aisle Security AnalysisWe found 2 potential security issue(s) in this PR:
1. 🔵 SSRF guard relaxed for Slack media downloads by allowing RFC2544 benchmark IP range (198.18.0.0/15)
DescriptionSlack media fetching now passes an SSRF policy that explicitly sets Impact:
Vulnerable code (new policy applied to Slack media fetches): const SLACK_MEDIA_SSRF_POLICY = {
allowedHostnames: ["*.slack.com", "*.slack-edge.com", "*.slack-files.com"],
allowRfc2544BenchmarkRange: true,
};Data flow:
RecommendationDo not allow RFC2544 benchmarking addresses for Slack media fetching unless you have a very specific, controlled requirement.
Secure example: const SLACK_MEDIA_SSRF_POLICY = {
allowedHostnames: ["*.slack.com", "*.slack-edge.com", "*.slack-files.com"],
// keep RFC2544 (198.18.0.0/15) blocked
allowRfc2544BenchmarkRange: false,
};(Optionally) add a defense-in-depth unit test in 2. 🔵 SSRF guard relaxed for Discord media downloads by allowing RFC2544 benchmark IP range (198.18.0.0/15)
Description
This meaningfully relaxes SSRF protections:
Vulnerable code (policy enabling RFC2544 allowance): const DISCORD_MEDIA_SSRF_POLICY: SsrFPolicy = {
allowedHostnames: ["cdn.discordapp.com", "media.discordapp.net"],
allowRfc2544BenchmarkRange: true,
};Concrete bypass surface enabled by this option:
Impact depends on deployment networking, but 198.18/15 is commonly treated as “internal/non-public” and may route to test networks or network appliances; allowing it increases SSRF pivot potential. RecommendationDo not enable RFC2544 benchmark range access for inbound media downloads unless there is a documented, unavoidable requirement.
Example safer policy: const DISCORD_MEDIA_SSRF_POLICY: SsrFPolicy = {
hostnameAllowlist: ["cdn.discordapp.com", "media.discordapp.net"],
// leave allowRfc2544BenchmarkRange undefined/false
};If you truly must allow these hostnames even when they resolve to internal space, consider a more targeted exception (e.g., only in specific deployments) and add monitoring/auditing for any fetches whose resolved IP is non-global. Analyzed PR: #25475 at commit Last updated on: 2026-03-01T17:43:22Z |
…ronments (openclaw#25475) Cherry-pick of upstream 39a4512.
…ronments (openclaw#25475) * fix(discord,slack): add SSRF policy for media downloads in proxy environments Discord and Slack media downloads (attachments, stickers, forwarded images) call fetchRemoteMedia without any ssrfPolicy. When running behind a local transparent proxy (Clash, mihomo, Shadowrocket) in fake-ip mode, DNS returns virtual IPs in the 198.18.0.0/15 range, which the SSRF guard blocks. Add per-channel SSRF policy constants—matching the pattern already applied to Telegram on main—that allowlist known CDN hostnames and set allowRfc2544BenchmarkRange: true. Refs openclaw#25355, openclaw#25322 Co-authored-by: Cursor <cursoragent@cursor.com> * chore(slack): keep raw-fetch allowlist line anchors stable --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
…ronments (openclaw#25475) * fix(discord,slack): add SSRF policy for media downloads in proxy environments Discord and Slack media downloads (attachments, stickers, forwarded images) call fetchRemoteMedia without any ssrfPolicy. When running behind a local transparent proxy (Clash, mihomo, Shadowrocket) in fake-ip mode, DNS returns virtual IPs in the 198.18.0.0/15 range, which the SSRF guard blocks. Add per-channel SSRF policy constants—matching the pattern already applied to Telegram on main—that allowlist known CDN hostnames and set allowRfc2544BenchmarkRange: true. Refs openclaw#25355, openclaw#25322 Co-authored-by: Cursor <cursoragent@cursor.com> * chore(slack): keep raw-fetch allowlist line anchors stable --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
…ronments (openclaw#25475) * fix(discord,slack): add SSRF policy for media downloads in proxy environments Discord and Slack media downloads (attachments, stickers, forwarded images) call fetchRemoteMedia without any ssrfPolicy. When running behind a local transparent proxy (Clash, mihomo, Shadowrocket) in fake-ip mode, DNS returns virtual IPs in the 198.18.0.0/15 range, which the SSRF guard blocks. Add per-channel SSRF policy constants—matching the pattern already applied to Telegram on main—that allowlist known CDN hostnames and set allowRfc2544BenchmarkRange: true. Refs openclaw#25355, openclaw#25322 Co-authored-by: Cursor <cursoragent@cursor.com> * chore(slack): keep raw-fetch allowlist line anchors stable --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
…ronments (openclaw#25475) * fix(discord,slack): add SSRF policy for media downloads in proxy environments Discord and Slack media downloads (attachments, stickers, forwarded images) call fetchRemoteMedia without any ssrfPolicy. When running behind a local transparent proxy (Clash, mihomo, Shadowrocket) in fake-ip mode, DNS returns virtual IPs in the 198.18.0.0/15 range, which the SSRF guard blocks. Add per-channel SSRF policy constants—matching the pattern already applied to Telegram on main—that allowlist known CDN hostnames and set allowRfc2544BenchmarkRange: true. Refs openclaw#25355, openclaw#25322 Co-authored-by: Cursor <cursoragent@cursor.com> * chore(slack): keep raw-fetch allowlist line anchors stable --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
…ronments (openclaw#25475) * fix(discord,slack): add SSRF policy for media downloads in proxy environments Discord and Slack media downloads (attachments, stickers, forwarded images) call fetchRemoteMedia without any ssrfPolicy. When running behind a local transparent proxy (Clash, mihomo, Shadowrocket) in fake-ip mode, DNS returns virtual IPs in the 198.18.0.0/15 range, which the SSRF guard blocks. Add per-channel SSRF policy constants—matching the pattern already applied to Telegram on main—that allowlist known CDN hostnames and set allowRfc2544BenchmarkRange: true. Refs openclaw#25355, openclaw#25322 Co-authored-by: Cursor <cursoragent@cursor.com> * chore(slack): keep raw-fetch allowlist line anchors stable --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
…ronments (openclaw#25475) * fix(discord,slack): add SSRF policy for media downloads in proxy environments Discord and Slack media downloads (attachments, stickers, forwarded images) call fetchRemoteMedia without any ssrfPolicy. When running behind a local transparent proxy (Clash, mihomo, Shadowrocket) in fake-ip mode, DNS returns virtual IPs in the 198.18.0.0/15 range, which the SSRF guard blocks. Add per-channel SSRF policy constants—matching the pattern already applied to Telegram on main—that allowlist known CDN hostnames and set allowRfc2544BenchmarkRange: true. Refs openclaw#25355, openclaw#25322 Co-authored-by: Cursor <cursoragent@cursor.com> * chore(slack): keep raw-fetch allowlist line anchors stable --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
…ronments (openclaw#25475) * fix(discord,slack): add SSRF policy for media downloads in proxy environments Discord and Slack media downloads (attachments, stickers, forwarded images) call fetchRemoteMedia without any ssrfPolicy. When running behind a local transparent proxy (Clash, mihomo, Shadowrocket) in fake-ip mode, DNS returns virtual IPs in the 198.18.0.0/15 range, which the SSRF guard blocks. Add per-channel SSRF policy constants—matching the pattern already applied to Telegram on main—that allowlist known CDN hostnames and set allowRfc2544BenchmarkRange: true. Refs openclaw#25355, openclaw#25322 Co-authored-by: Cursor <cursoragent@cursor.com> * chore(slack): keep raw-fetch allowlist line anchors stable --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
…ronments (openclaw#25475) * fix(discord,slack): add SSRF policy for media downloads in proxy environments Discord and Slack media downloads (attachments, stickers, forwarded images) call fetchRemoteMedia without any ssrfPolicy. When running behind a local transparent proxy (Clash, mihomo, Shadowrocket) in fake-ip mode, DNS returns virtual IPs in the 198.18.0.0/15 range, which the SSRF guard blocks. Add per-channel SSRF policy constants—matching the pattern already applied to Telegram on main—that allowlist known CDN hostnames and set allowRfc2544BenchmarkRange: true. Refs openclaw#25355, openclaw#25322 Co-authored-by: Cursor <cursoragent@cursor.com> * chore(slack): keep raw-fetch allowlist line anchors stable --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
…ronments (openclaw#25475) * fix(discord,slack): add SSRF policy for media downloads in proxy environments Discord and Slack media downloads (attachments, stickers, forwarded images) call fetchRemoteMedia without any ssrfPolicy. When running behind a local transparent proxy (Clash, mihomo, Shadowrocket) in fake-ip mode, DNS returns virtual IPs in the 198.18.0.0/15 range, which the SSRF guard blocks. Add per-channel SSRF policy constants—matching the pattern already applied to Telegram on main—that allowlist known CDN hostnames and set allowRfc2544BenchmarkRange: true. Refs openclaw#25355, openclaw#25322 Co-authored-by: Cursor <cursoragent@cursor.com> * chore(slack): keep raw-fetch allowlist line anchors stable --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
…ronments (openclaw#25475) * fix(discord,slack): add SSRF policy for media downloads in proxy environments Discord and Slack media downloads (attachments, stickers, forwarded images) call fetchRemoteMedia without any ssrfPolicy. When running behind a local transparent proxy (Clash, mihomo, Shadowrocket) in fake-ip mode, DNS returns virtual IPs in the 198.18.0.0/15 range, which the SSRF guard blocks. Add per-channel SSRF policy constants—matching the pattern already applied to Telegram on main—that allowlist known CDN hostnames and set allowRfc2544BenchmarkRange: true. Refs openclaw#25355, openclaw#25322 Co-authored-by: Cursor <cursoragent@cursor.com> * chore(slack): keep raw-fetch allowlist line anchors stable --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
…ronments (openclaw#25475) * fix(discord,slack): add SSRF policy for media downloads in proxy environments Discord and Slack media downloads (attachments, stickers, forwarded images) call fetchRemoteMedia without any ssrfPolicy. When running behind a local transparent proxy (Clash, mihomo, Shadowrocket) in fake-ip mode, DNS returns virtual IPs in the 198.18.0.0/15 range, which the SSRF guard blocks. Add per-channel SSRF policy constants—matching the pattern already applied to Telegram on main—that allowlist known CDN hostnames and set allowRfc2544BenchmarkRange: true. Refs openclaw#25355, openclaw#25322 Co-authored-by: Cursor <cursoragent@cursor.com> * chore(slack): keep raw-fetch allowlist line anchors stable --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
…ronments (openclaw#25475) Cherry-pick of upstream 39a4512.
Summary
fetchRemoteMedia()without anyssrfPolicy. When running behind a local transparent proxy (Clash, mihomo, Shadowrocket) in fake-ip mode, DNS returns virtual IPs in the198.18.0.0/15(RFC 2544) range, and the SSRF guard blocks them — making all inbound media invisible to the agent.DISCORD_MEDIA_SSRF_POLICY,SLACK_MEDIA_SSRF_POLICY) that allowlist known CDN hostnames and setallowRfc2544BenchmarkRange: true, then passed them to everyfetchRemoteMedia()call site in the Discord and Slack monitor paths.SsrFPolicytype, orfetchWithSsrFGuard. Telegram already has its own policy onmainand is not touched. Theweb_fetchtool path is handled separately by PR fix(web-fetch): expose ssrfPolicy.allowRfc2544BenchmarkRange config option #25258.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
Discord and Slack inbound media (attachments, stickers, forwarded images) now download successfully when DNS resolves CDN hostnames to
198.18.x.xvirtual IPs (common with Clash/mihomo/Shadowrocket fake-ip mode). No config changes required — the fix is automatic.Security Impact (required)
NoNoYes— previously blocked fetches to Discord/Slack CDNs now succeed when DNS resolves to RFC 2544 benchmark range IPs.NoNocdn.discordapp.com,media.discordapp.netfor Discord;*.slack.com,*.slack-edge.com,*.slack-files.comfor Slack. Requests to other hosts remain fully guarded.198.18.0.0/15) — all other private/internal/special-use IP ranges remain blocked.TELEGRAM_MEDIA_SSRF_POLICYalready onmain.Repro + Verification
Environment
cdn.discordapp.comresolves to198.18.0.44Steps
.txtor image attachment in DiscordExpected
Actual
security: blocked URL fetch … target=https://cdn.discordapp.com … reason=Blocked: resolves to private/internal/special-use IP addressEvidence
All 14 Discord monitor tests pass (including 1 new + 3 updated assertions).
All 26 Slack monitor tests pass (including 2 new SSRF policy tests).
Human Verification (required)
mainand is not regressed.Compatibility / Migration
YesNoNoFailure Recovery (if this breaks)
src/discord/monitor/message-utils.ts,src/slack/monitor/media.tsallowedHostnameslist would need updating.Risks and Mitigations
allowedHostnameslist is tightly scoped to first-party CDN domains controlled by Discord/Slack. The same trust model is already applied to Telegram'sapi.telegram.orgonmain.