





















@@ -37,6 +37,7 @@ const {
3737 globalFetchMock,
3838 HttpsAgent,
3939 HttpsProxyAgent,
40+ fetchWithSsrFGuardMock,
4041 getLastAgent,
4142 getLastProxyAgent,
4243 resolveDebugProxySettingsMock,
@@ -53,6 +54,18 @@ const {
5354const captureHttpExchangeSpy = vi.fn();
5455const captureWsEventSpy = vi.fn();
5556const resolveDebugProxySettingsMock = vi.fn(() => ({ enabled: false }));
57+const fetchWithSsrFGuardMock = vi.fn(async (params: { url: string; init?: RequestInit }) => {
58+const source = (await globalFetchMock(params.url, params.init)) as Response;
59+const body = await source.text();
60+return {
61+response: new Response(body, {
62+status: source.status,
63+statusText: source.statusText,
64+headers: source.headers,
65+}),
66+release: vi.fn(),
67+};
68+});
56695770const GatewayIntents = {
5871Guilds: 1 << 0,
@@ -114,6 +127,7 @@ const {
114127 globalFetchMock,
115128 HttpsAgent,
116129 HttpsProxyAgent,
130+ fetchWithSsrFGuardMock,
117131getLastAgent: () => HttpsAgent.lastCreated,
118132getLastProxyAgent: () => HttpsProxyAgent.lastCreated,
119133 captureHttpExchangeSpy,
@@ -166,18 +180,7 @@ vi.mock("openclaw/plugin-sdk/proxy-capture", () => ({
166180}));
167181168182vi.mock("openclaw/plugin-sdk/ssrf-runtime", () => ({
169-fetchWithSsrFGuard: vi.fn(async (params: { url: string; init?: RequestInit }) => {
170-const source = (await globalFetchMock(params.url, params.init)) as Response;
171-const body = await source.text();
172-return {
173-response: new Response(body, {
174-status: source.status,
175-statusText: source.statusText,
176-headers: source.headers,
177-}),
178-release: vi.fn(),
179-};
180-}),
183+fetchWithSsrFGuard: fetchWithSsrFGuardMock,
181184}));
182185183186describe("createDiscordGatewayPlugin", () => {
@@ -213,6 +216,16 @@ describe("createDiscordGatewayPlugin", () => {
213216return firstMockCall(mock, label)[index];
214217}
215218219+function firstGuardedFetchCall() {
220+return firstMockArg(fetchWithSsrFGuardMock, "fetchWithSsrFGuardMock") as {
221+url: string;
222+init?: RequestInit & { signal?: unknown };
223+mode?: string;
224+dispatcherPolicy?: unknown;
225+policy?: unknown;
226+};
227+}
228+216229function createProxyTestingOverrides() {
217230return {
218231HttpsProxyAgentCtor:
@@ -318,6 +331,7 @@ describe("createDiscordGatewayPlugin", () => {
318331vi.useRealTimers();
319332baseRegisterClientSpy.mockClear();
320333globalFetchMock.mockClear();
334+fetchWithSsrFGuardMock.mockClear();
321335httpsAgentSpy.mockClear();
322336wsProxyAgentSpy.mockClear();
323337webSocketSpy.mockClear();
@@ -504,9 +518,10 @@ describe("createDiscordGatewayPlugin", () => {
504518expect(Object.getPrototypeOf(plugin)).not.toBe(GatewayPlugin.prototype);
505519expect(runtime.error).toHaveBeenCalled();
506520expect(runtime.log).not.toHaveBeenCalled();
521+expect(fetchWithSsrFGuardMock).not.toHaveBeenCalled();
507522});
508523509-it("keeps gateway metadata lookup on the guarded direct fetch when proxy is configured", async () => {
524+it("routes gateway metadata lookup through the guarded proxy dispatcher", async () => {
510525const runtime = createRuntime();
511526const plugin = createDiscordGatewayPlugin({
512527discordConfig: { proxy: "http://127.0.0.1:8080" },
@@ -516,15 +531,18 @@ describe("createDiscordGatewayPlugin", () => {
516531517532await registerGatewayClientWithMetadata({ plugin, fetchMock: globalFetchMock });
518533519-expect(globalFetchMock).toHaveBeenCalledTimes(1);
520-const fetchInit = firstMockArg(globalFetchMock, "globalFetchMock", 1) as
521-| { headers?: Record<string, string>; signal?: unknown }
522-| undefined;
523-expect(firstMockArg(globalFetchMock, "globalFetchMock")).toBe(
524-"https://discord.com/api/v10/gateway/bot",
525-);
526-expect(fetchInit?.headers).toEqual({ Authorization: "Bot token-123" });
527-expect(fetchInit?.signal).toBeInstanceOf(AbortSignal);
534+expect(fetchWithSsrFGuardMock).toHaveBeenCalledTimes(1);
535+const guardedFetch = firstGuardedFetchCall();
536+expect(guardedFetch.url).toBe("https://discord.com/api/v10/gateway/bot");
537+expect(guardedFetch.mode).toBe("trusted_explicit_proxy");
538+expect(guardedFetch.dispatcherPolicy).toEqual({
539+mode: "explicit-proxy",
540+proxyUrl: "http://127.0.0.1:8080",
541+allowPrivateProxy: true,
542+});
543+expect(guardedFetch.policy).toEqual({ allowedHostnames: ["discord.com"] });
544+expect(guardedFetch.init?.headers).toEqual({ Authorization: "Bot token-123" });
545+expect(guardedFetch.init?.signal).toBeInstanceOf(AbortSignal);
528546expect(baseRegisterClientSpy).toHaveBeenCalledTimes(1);
529547});
530548此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。