@@ -11,7 +11,36 @@ vi.mock("openclaw/plugin-sdk/ssrf-runtime", () => ({
|
11 | 11 | resolvePinnedHostnameWithPolicyMock(...args), |
12 | 12 | })); |
13 | 13 | |
14 | | -import { deleteWebhook, getWebhookInfo, sendChatAction, sendPhoto, type ZaloFetch } from "./api.js"; |
| 14 | +import { |
| 15 | +deleteWebhook, |
| 16 | +getMe, |
| 17 | +getWebhookInfo, |
| 18 | +sendChatAction, |
| 19 | +sendPhoto, |
| 20 | +type ZaloFetch, |
| 21 | +} from "./api.js"; |
| 22 | + |
| 23 | +const ZALO_JSON_CAP_BYTES = 16 * 1024 * 1024; |
| 24 | + |
| 25 | +function oversizedZaloJsonResponse(onCancel: () => void): Response { |
| 26 | +const response = new Response( |
| 27 | +new ReadableStream<Uint8Array>({ |
| 28 | +start(controller) { |
| 29 | +controller.enqueue(new Uint8Array(ZALO_JSON_CAP_BYTES + 1)); |
| 30 | +}, |
| 31 | +cancel() { |
| 32 | +onCancel(); |
| 33 | +}, |
| 34 | +}), |
| 35 | +{ headers: { "content-type": "application/json" }, status: 200 }, |
| 36 | +); |
| 37 | +Object.defineProperty(response, "json", { |
| 38 | +value: async () => { |
| 39 | +throw new Error("unbounded json reader was used"); |
| 40 | +}, |
| 41 | +}); |
| 42 | +return response; |
| 43 | +} |
15 | 44 | |
16 | 45 | function createOkFetcher() { |
17 | 46 | return vi.fn<ZaloFetch>(async () => new Response(JSON.stringify({ ok: true, result: {} }))); |
@@ -103,10 +132,7 @@ describe("Zalo API request methods", () => {
|
103 | 132 | .mockImplementation(() => undefined); |
104 | 133 | try { |
105 | 134 | const fetcher = vi.fn<ZaloFetch>( |
106 | | -async () => |
107 | | -({ |
108 | | -json: async () => ({ ok: true, result: {} }), |
109 | | -}) as Response, |
| 135 | +async () => new Response(JSON.stringify({ ok: true, result: {} })), |
110 | 136 | ); |
111 | 137 | |
112 | 138 | await sendChatAction( |
@@ -199,4 +225,18 @@ describe("Zalo API request methods", () => {
|
199 | 225 | expect(resolvePinnedHostnameWithPolicyMock).not.toHaveBeenCalled(); |
200 | 226 | expect(fetcher).not.toHaveBeenCalled(); |
201 | 227 | }); |
| 228 | + |
| 229 | +it("bounds oversized getMe JSON responses and cancels the stream", async () => { |
| 230 | +let cancelCount = 0; |
| 231 | +const fetcher = vi.fn<ZaloFetch>(async () => |
| 232 | +oversizedZaloJsonResponse(() => { |
| 233 | +cancelCount += 1; |
| 234 | +}), |
| 235 | +); |
| 236 | + |
| 237 | +await expect(getMe("test-token", undefined, fetcher)).rejects.toThrow( |
| 238 | +"zalo.getMe: JSON response exceeds 16777216 bytes", |
| 239 | +); |
| 240 | +expect(cancelCount).toBe(1); |
| 241 | +}); |
202 | 242 | }); |