From 0f18e829323e671bbbc6bf64a37ae42ca81a9b56 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 20 Jun 2026 04:20:02 +0800 Subject: [PATCH] fix(e2e): reject unsafe bounded response text lengths Reject unsafe decimal Content-Length values in the E2E bounded response text helper before streaming response bodies. Keep non-decimal values on the streaming byte-limit path and add regression coverage proving unsafe declared lengths cancel without starting a read. Proof: direct patched repro rejects before reading with code ETOOBIG; origin/main comparison entered the reader first; node --check scripts/e2e/lib/bounded-response-text.mjs; git diff --check origin/main...HEAD; autoreview clean overall 0.86; exact-head release gate succeeded at https://github.com/openclaw/openclaw/actions/runs/27846197115. --- scripts/e2e/lib/bounded-response-text.mjs | 2 +- test/scripts/bounded-response-text.test.ts | 32 ++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/scripts/e2e/lib/bounded-response-text.mjs b/scripts/e2e/lib/bounded-response-text.mjs index 70a59816d26..7bb05033fac 100644 --- a/scripts/e2e/lib/bounded-response-text.mjs +++ b/scripts/e2e/lib/bounded-response-text.mjs @@ -17,7 +17,7 @@ function parseContentLengthHeader(headers) { return undefined; } const parsed = Number(raw); - return Number.isSafeInteger(parsed) ? parsed : undefined; + return Number.isSafeInteger(parsed) ? parsed : Number.POSITIVE_INFINITY; } export async function readBoundedResponseText(response, label, byteLimit, timeoutPromise) { diff --git a/test/scripts/bounded-response-text.test.ts b/test/scripts/bounded-response-text.test.ts index cc06d818c15..1a4057b76ca 100644 --- a/test/scripts/bounded-response-text.test.ts +++ b/test/scripts/bounded-response-text.test.ts @@ -111,4 +111,36 @@ describe("scripts/e2e/lib/bounded-response-text.mjs", () => { expect(readStarted).toBe(true); expect(canceled).toBe(true); }); + + it("rejects unsafe decimal content-length values before reading", async () => { + let readStarted = false; + let canceled = false; + const response = { + headers: new Headers({ "content-length": "9007199254740993" }), + body: { + async cancel() { + canceled = true; + }, + getReader() { + return { + async read() { + readStarted = true; + return new Promise>(() => {}); + }, + async cancel() { + canceled = true; + }, + releaseLock() {}, + }; + }, + }, + }; + + await expect(readBoundedResponseText(response, "probe", 16)).rejects.toMatchObject({ + code: "ETOOBIG", + message: "probe response body exceeded 16 bytes", + }); + expect(readStarted).toBe(false); + expect(canceled).toBe(true); + }); });