From 906174bff16dc82631c7e6a43860436aa6e38cdb Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Thu, 18 Jun 2026 21:20:26 +0200 Subject: [PATCH] fix(scripts): ignore loose audit content length headers --- scripts/pre-commit/pnpm-audit-prod.mjs | 6 ++++-- test/scripts/pnpm-audit-prod.test.ts | 27 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/scripts/pre-commit/pnpm-audit-prod.mjs b/scripts/pre-commit/pnpm-audit-prod.mjs index ad59926731fc..d68b6c9078a0 100644 --- a/scripts/pre-commit/pnpm-audit-prod.mjs +++ b/scripts/pre-commit/pnpm-audit-prod.mjs @@ -729,8 +729,10 @@ async function withBulkAdvisoryTimeout({ label, timeoutMs, run }) { } async function readBoundedResponseText(response, maxBytes, label) { - const contentLength = Number.parseInt(response.headers?.get?.("content-length") ?? "", 10); - if (Number.isFinite(contentLength) && contentLength > maxBytes) { + const rawContentLength = response.headers?.get?.("content-length"); + const contentLength = + rawContentLength && /^\d+$/u.test(rawContentLength) ? Number(rawContentLength) : undefined; + if (Number.isSafeInteger(contentLength) && contentLength > maxBytes) { await response.body?.cancel().catch(() => undefined); throw Object.assign(new Error(`${label} exceeded ${maxBytes} bytes`), { code: "ETOOBIG" }); } diff --git a/test/scripts/pnpm-audit-prod.test.ts b/test/scripts/pnpm-audit-prod.test.ts index 88fe1c32a019..ce681cc53b07 100644 --- a/test/scripts/pnpm-audit-prod.test.ts +++ b/test/scripts/pnpm-audit-prod.test.ts @@ -297,6 +297,33 @@ snapshots: expect(cancelled).toBe(true); }); + it("streams non-decimal bulk advisory content-length values through the body cap", async () => { + let readStarted = false; + let cancelled = false; + const body = new ReadableStream({ + pull(controller) { + readStarted = true; + controller.enqueue(new TextEncoder().encode("12345")); + }, + cancel() { + cancelled = true; + }, + }); + const request = fetchBulkAdvisories({ + payload: { axios: ["1.0.0"] }, + responseBodyMaxBytes: 4, + fetchImpl: async () => + new Response(body, { + status: 200, + headers: { "content-length": "5junk" }, + }), + }); + + await expect(request).rejects.toThrow(/Bulk advisory response body exceeded 4 bytes/u); + expect(readStarted).toBe(true); + expect(cancelled).toBe(true); + }); + it("fails closed on empty successful bulk advisory response bodies", async () => { const request = fetchBulkAdvisories({ payload: { axios: ["1.0.0"] },