From acb2a1ce37769e1fad01939d9d891bacb0aef28a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 16 Feb 2026 00:15:24 +0000 Subject: [PATCH] perf(test): fold discord voice hardening into web media suite --- .../send.voice-message.security.test.ts | 24 ------------------- src/web/media.test.ts | 22 +++++++++++++++++ 2 files changed, 22 insertions(+), 24 deletions(-) delete mode 100644 src/discord/send.voice-message.security.test.ts diff --git a/src/discord/send.voice-message.security.test.ts b/src/discord/send.voice-message.security.test.ts deleted file mode 100644 index 9651f57c5ae..00000000000 --- a/src/discord/send.voice-message.security.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import path from "node:path"; -import { describe, expect, it } from "vitest"; -import { sendVoiceMessageDiscord } from "./send.js"; - -describe("sendVoiceMessageDiscord - media hardening", () => { - it("rejects local paths outside allowed media roots (prevents local file exfiltration)", async () => { - const candidate = path.join(process.cwd(), "package.json"); - await expect(sendVoiceMessageDiscord("channel:123", candidate)).rejects.toThrow( - /Local media path is not under an allowed directory/, - ); - }); - - it("blocks SSRF targets when given a private-network URL", async () => { - await expect( - sendVoiceMessageDiscord("channel:123", "http://127.0.0.1/voice.ogg"), - ).rejects.toThrow(/Failed to fetch media|Blocked/); - }); - - it("does not allow non-http URL schemes to reach ffmpeg/ffprobe", async () => { - await expect( - sendVoiceMessageDiscord("channel:123", "rtsp://example.com/voice.ogg"), - ).rejects.toThrow(/Local media path is not under an allowed directory|ENOENT|no such file/i); - }); -}); diff --git a/src/web/media.test.ts b/src/web/media.test.ts index fadcea5cceb..ebf9fa0d4a3 100644 --- a/src/web/media.test.ts +++ b/src/web/media.test.ts @@ -3,6 +3,7 @@ import os from "node:os"; import path from "node:path"; import sharp from "sharp"; import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest"; +import { sendVoiceMessageDiscord } from "../discord/send.js"; import * as ssrf from "../infra/net/ssrf.js"; import { optimizeImageToPng } from "../media/image-ops.js"; import { captureEnv } from "../test-utils/env.js"; @@ -333,6 +334,27 @@ describe("web media loading", () => { }); }); +describe("Discord voice message input hardening", () => { + it("rejects local paths outside allowed media roots", async () => { + const candidate = path.join(process.cwd(), "package.json"); + await expect(sendVoiceMessageDiscord("channel:123", candidate)).rejects.toThrow( + /Local media path is not under an allowed directory/i, + ); + }); + + it("blocks SSRF targets when given a private-network URL", async () => { + await expect( + sendVoiceMessageDiscord("channel:123", "http://127.0.0.1/voice.ogg"), + ).rejects.toThrow(/Failed to fetch media|Blocked|private|internal/i); + }); + + it("rejects non-http URL schemes", async () => { + await expect( + sendVoiceMessageDiscord("channel:123", "rtsp://example.com/voice.ogg"), + ).rejects.toThrow(/Local media path is not under an allowed directory|ENOENT|no such file/i); + }); +}); + describe("local media root guard", () => { it("rejects local paths outside allowed roots", async () => { // Explicit roots that don't contain the temp file.