Skip to content

Commit 7ba9212

Browse files
hugenshenNIOPeter Steinberger
authored
Fix/discord bound probe getme json reads (#97278)
* fix(discord): bound probe getMe JSON response reads * test(discord): add oversized probe getMe JSON regression * test(discord): add loopback proof for bounded probe getMe reads * fix(scripts): satisfy oxlint in discord probe proof script * test(discord): keep probe proof in focused coverage --------- Co-authored-by: NIO <[email protected]> Co-authored-by: Peter Steinberger <[email protected]>
1 parent 6c7a6ff commit 7ba9212

2 files changed

Lines changed: 42 additions & 1 deletion

File tree

extensions/discord/src/probe.intents.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,28 @@ import {
99
} from "./probe.js";
1010
import { jsonResponse } from "./test-http-helpers.js";
1111

12+
const DISCORD_PROBE_JSON_CAP_BYTES = 16 * 1024 * 1024;
13+
14+
function oversizedDiscordProbeJsonResponse(onCancel: () => void): Response {
15+
const response = new Response(
16+
new ReadableStream<Uint8Array>({
17+
start(controller) {
18+
controller.enqueue(new Uint8Array(DISCORD_PROBE_JSON_CAP_BYTES + 1));
19+
},
20+
cancel() {
21+
onCancel();
22+
},
23+
}),
24+
{ headers: { "content-type": "application/json" }, status: 200 },
25+
);
26+
Object.defineProperty(response, "json", {
27+
value: async () => {
28+
throw new Error("unbounded json reader was used");
29+
},
30+
});
31+
return response;
32+
}
33+
1234
describe("resolveDiscordPrivilegedIntentsFromFlags", () => {
1335
beforeEach(() => {
1436
vi.useRealTimers();
@@ -104,6 +126,21 @@ describe("resolveDiscordPrivilegedIntentsFromFlags", () => {
104126
expect(cancel).toHaveBeenCalledTimes(1);
105127
});
106128

129+
it("bounds oversized getMe probe JSON responses and cancels the stream", async () => {
130+
let cancelCount = 0;
131+
const fetcher = withFetchPreconnect(async () =>
132+
oversizedDiscordProbeJsonResponse(() => {
133+
cancelCount += 1;
134+
}),
135+
);
136+
137+
await expect(probeDiscord("MTIz.abc.def", 1_000, { fetcher })).resolves.toMatchObject({
138+
ok: false,
139+
error: expect.stringContaining("discord.probe.getMe: JSON response exceeds 16777216 bytes"),
140+
});
141+
expect(cancelCount).toBe(1);
142+
});
143+
107144
it("derives application id from parseable tokens before probing REST", async () => {
108145
let calls = 0;
109146
const fetcher = withFetchPreconnect(async () => {

extensions/discord/src/probe.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import type { BaseProbeResult } from "openclaw/plugin-sdk/channel-contract";
33
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
44
import { resolveFetch } from "openclaw/plugin-sdk/fetch-runtime";
5+
import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
56
import { fetchWithTimeout } from "openclaw/plugin-sdk/text-utility-runtime";
67
import { DiscordApiError, fetchDiscord } from "./api.js";
78
import { normalizeDiscordToken } from "./token.js";
@@ -155,7 +156,10 @@ export async function probeDiscord(
155156
result.error = `getMe failed (${res.status})`;
156157
return { ...result, elapsedMs: Date.now() - started };
157158
}
158-
const json = (await res.json()) as { id?: string; username?: string };
159+
const json = await readProviderJsonResponse<{ id?: string; username?: string }>(
160+
res,
161+
"discord.probe.getMe",
162+
);
159163
result.ok = true;
160164
result.bot = {
161165
id: json.id ?? null,

0 commit comments

Comments
 (0)