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);