From 44fea3c94abe69fce393021c7c82f30f07596a86 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Wed, 3 Jun 2026 12:00:52 +0200 Subject: [PATCH] fix(tooling): cancel oversized audit responses --- CHANGELOG.md | 1 + scripts/pre-commit/pnpm-audit-prod.mjs | 1 + test/scripts/pnpm-audit-prod.test.ts | 12 +++++++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3edaf91a8c6..28e888abaa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ Docs: https://docs.openclaw.ai - Release/CI/E2E: write package Telegram Docker artifacts to unique per-run directories by default so parallel live/RTT runs cannot overwrite evidence. - Release/CI/E2E: keep plugin lifecycle matrix resource artifacts under a unique per-run scratch root so parallel runs cannot overwrite tarballs or inspect output. - Release/CI/E2E: bound mock OpenAI readiness probes in web-search and Telegram RTT Docker smokes so stalled HTTP accepts cannot hang cleanup or fall through. +- Tooling: cancel oversized pnpm audit advisory responses before failing so registry error paths do not leave response bodies open. - Release/CI/E2E: fail secret-provider proof runs when temporary state cleanup still fails after retries instead of hiding the cleanup error. - Release/CI/E2E: fail package-candidate ref proofs when temporary source worktree cleanup fails instead of leaving stale worktrees behind. - Release/CI/E2E: remove package tarball extract directories when tar extraction fails before validation can continue. diff --git a/scripts/pre-commit/pnpm-audit-prod.mjs b/scripts/pre-commit/pnpm-audit-prod.mjs index 890cd9f2a84..bbbd365d253 100644 --- a/scripts/pre-commit/pnpm-audit-prod.mjs +++ b/scripts/pre-commit/pnpm-audit-prod.mjs @@ -729,6 +729,7 @@ 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) { + 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 6f0a0725bd0..269f42a4a2c 100644 --- a/test/scripts/pnpm-audit-prod.test.ts +++ b/test/scripts/pnpm-audit-prod.test.ts @@ -259,17 +259,27 @@ snapshots: }); it("bounds successful bulk advisory response bodies", async () => { + let cancelled = false; + const body = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("{}")); + }, + cancel() { + cancelled = true; + }, + }); const request = fetchBulkAdvisories({ payload: { axios: ["1.0.0"] }, responseBodyMaxBytes: 4, fetchImpl: async () => - new Response("{}", { + new Response(body, { status: 200, headers: { "content-length": "5" }, }), }); await expect(request).rejects.toThrow(/Bulk advisory response body exceeded 4 bytes/u); + expect(cancelled).toBe(true); }); it("fails closed on empty successful bulk advisory response bodies", async () => {