From 9e337073c910bebe38f47ed824bb72ec671306bf Mon Sep 17 00:00:00 2001 From: Alix-007 Date: Mon, 29 Jun 2026 02:19:47 +0800 Subject: [PATCH] fix(openrouter): bound video response reads Route OpenRouter video submit and poll success JSON through the shared bounded provider JSON reader, preserving malformed-response mapping and SSRF request policy coverage. (cherry picked from commit 48f34b1d4df7755a19894f1e763011bb8bd328ea) --- .../video-generation-provider.test.ts | 26 +++++++++++++++---- .../openrouter/video-generation-provider.ts | 10 ++++--- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/extensions/openrouter/video-generation-provider.test.ts b/extensions/openrouter/video-generation-provider.test.ts index 4ade52de4f7a..8cb7a95b6635 100644 --- a/extensions/openrouter/video-generation-provider.test.ts +++ b/extensions/openrouter/video-generation-provider.test.ts @@ -546,6 +546,7 @@ describe("openrouter video generation provider", () => { provider: "openrouter", capability: "video", baseUrl: "https://custom.openrouter.test/api/v1", + allowPrivateNetwork: false, request: requestOverrides, }, ); @@ -672,11 +673,10 @@ describe("openrouter video generation provider", () => { it("wraps non-JSON successful OpenRouter submit responses", async () => { postJsonRequestMock.mockResolvedValue({ - response: { - json: async () => { - throw new SyntaxError("Unexpected token < in JSON"); - }, - }, + response: new Response("", { + status: 200, + headers: { "content-type": "text/html" }, + }), release: vi.fn(async () => {}), }); @@ -691,6 +691,22 @@ describe("openrouter video generation provider", () => { ).rejects.toThrow("OpenRouter video generation response malformed"); }); + it("bounds oversized successful OpenRouter submit responses", async () => { + const oversized = releasedOversizedJsonStream(); + postJsonRequestMock.mockResolvedValue(oversized); + + const provider = buildOpenRouterVideoGenerationProvider(); + await expect( + provider.generateVideo({ + provider: "openrouter", + model: "google/veo-3.1", + prompt: "oversized body", + cfg: {} as never, + }), + ).rejects.toThrow("OpenRouter video generation: JSON response exceeds 16777216 bytes"); + expect(oversized.wasCanceled()).toBe(true); + }); + it("rejects unknown OpenRouter poll statuses without waiting for timeout", async () => { postJsonRequestMock.mockResolvedValue( releasedJson({ diff --git a/extensions/openrouter/video-generation-provider.ts b/extensions/openrouter/video-generation-provider.ts index 66e08202fdaf..232672e5cc7b 100644 --- a/extensions/openrouter/video-generation-provider.ts +++ b/extensions/openrouter/video-generation-provider.ts @@ -6,6 +6,7 @@ import { assertOkOrThrowHttpError, createProviderOperationDeadline, postJsonRequest, + readProviderJsonResponse, resolveProviderHttpRequestConfig, resolveProviderOperationTimeoutMs, sanitizeConfiguredModelProviderRequest, @@ -68,9 +69,12 @@ type OpenRouterFrameImagePart = OpenRouterImagePart & { async function readOpenRouterVideoJson(response: Response): Promise> { let payload: unknown; try { - payload = await response.json(); - } catch { - throw new Error(OPENROUTER_VIDEO_MALFORMED_RESPONSE); + payload = await readProviderJsonResponse(response, "OpenRouter video generation"); + } catch (error) { + if (error instanceof Error && error.message.endsWith(": malformed JSON response")) { + throw new Error(OPENROUTER_VIDEO_MALFORMED_RESPONSE, { cause: error }); + } + throw error; } if (!isRecord(payload)) { throw new Error(OPENROUTER_VIDEO_MALFORMED_RESPONSE);