Skip to content

Commit 6442ff9

Browse files
committed
fix(discord): add SSRF policy for Discord CDN to unblock Clash TUN fake-ip
1 parent 85b075d commit 6442ff9

File tree

2 files changed

+22
-0
lines changed

2 files changed

+22
-0
lines changed

src/discord/monitor/message-utils.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ describe("resolveForwardedMediaList", () => {
9494
filePathHint: attachment.filename,
9595
maxBytes: 512,
9696
fetchImpl: undefined,
97+
ssrfPolicy: {
98+
allowedHostnames: ["cdn.discordapp.com", "media.discordapp.net"],
99+
allowRfc2544BenchmarkRange: true,
100+
},
97101
});
98102
expect(saveMediaBuffer).toHaveBeenCalledTimes(1);
99103
expect(saveMediaBuffer).toHaveBeenCalledWith(expect.any(Buffer), "image/png", "inbound", 512);
@@ -168,6 +172,10 @@ describe("resolveForwardedMediaList", () => {
168172
filePathHint: "wave.png",
169173
maxBytes: 512,
170174
fetchImpl: undefined,
175+
ssrfPolicy: {
176+
allowedHostnames: ["cdn.discordapp.com", "media.discordapp.net"],
177+
allowRfc2544BenchmarkRange: true,
178+
},
171179
});
172180
expect(saveMediaBuffer).toHaveBeenCalledTimes(1);
173181
expect(saveMediaBuffer).toHaveBeenCalledWith(expect.any(Buffer), "image/png", "inbound", 512);
@@ -236,6 +244,10 @@ describe("resolveMediaList", () => {
236244
filePathHint: "hello.png",
237245
maxBytes: 512,
238246
fetchImpl: undefined,
247+
ssrfPolicy: {
248+
allowedHostnames: ["cdn.discordapp.com", "media.discordapp.net"],
249+
allowRfc2544BenchmarkRange: true,
250+
},
239251
});
240252
expect(saveMediaBuffer).toHaveBeenCalledTimes(1);
241253
expect(saveMediaBuffer).toHaveBeenCalledWith(expect.any(Buffer), "image/png", "inbound", 512);

src/discord/monitor/message-utils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { ChannelType, Client, Message } from "@buape/carbon";
22
import { StickerFormatType, type APIAttachment, type APIStickerItem } from "discord-api-types/v10";
33
import { buildMediaPayload } from "../../channels/plugins/media-payload.js";
44
import { logVerbose } from "../../globals.js";
5+
import type { SsrFPolicy } from "../../infra/net/ssrf.js";
56
import { fetchRemoteMedia, type FetchLike } from "../../media/fetch.js";
67
import { saveMediaBuffer } from "../../media/store.js";
78

@@ -53,6 +54,13 @@ const DISCORD_CHANNEL_INFO_CACHE = new Map<
5354
>();
5455
const DISCORD_STICKER_ASSET_BASE_URL = "https://media.discordapp.net/stickers";
5556

57+
const DISCORD_MEDIA_SSRF_POLICY: SsrFPolicy = {
58+
// Discord CDN downloads should be trusted even when DNS/proxy resolution
59+
// maps to private/internal ranges (e.g. Clash TUN fake-ip 198.18.x.x).
60+
allowedHostnames: ["cdn.discordapp.com", "media.discordapp.net"],
61+
allowRfc2544BenchmarkRange: true,
62+
};
63+
5664
export function __resetDiscordChannelInfoCacheForTest() {
5765
DISCORD_CHANNEL_INFO_CACHE.clear();
5866
}
@@ -228,6 +236,7 @@ async function appendResolvedMediaFromAttachments(params: {
228236
filePathHint: attachment.filename ?? attachment.url,
229237
maxBytes: params.maxBytes,
230238
fetchImpl: params.fetchImpl,
239+
ssrfPolicy: DISCORD_MEDIA_SSRF_POLICY,
231240
});
232241
const saved = await saveMediaBuffer(
233242
fetched.buffer,
@@ -320,6 +329,7 @@ async function appendResolvedMediaFromStickers(params: {
320329
filePathHint: candidate.fileName,
321330
maxBytes: params.maxBytes,
322331
fetchImpl: params.fetchImpl,
332+
ssrfPolicy: DISCORD_MEDIA_SSRF_POLICY,
323333
});
324334
const saved = await saveMediaBuffer(
325335
fetched.buffer,

0 commit comments

Comments
 (0)