From 117e87df0d76594cbc4dcbe6c2e52f179288b2d1 Mon Sep 17 00:00:00 2001 From: can1357 Date: Thu, 18 Jun 2026 20:03:34 +0200 Subject: [PATCH] fix(coding-agent/utils): enhanced zip size validation checks - Replaced insufficient file size checks with comprehensive validation for ZIP header and length limits. - Added explicit rejection for archives that would exceed ZIP32 entry counts, name lengths, or total offsets. - Added validation for central directory size to prevent overflow before generating the EOCD record. --- packages/coding-agent/src/utils/zip.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/coding-agent/src/utils/zip.ts b/packages/coding-agent/src/utils/zip.ts index a89d20339..962c4e18c 100644 --- a/packages/coding-agent/src/utils/zip.ts +++ b/packages/coding-agent/src/utils/zip.ts @@ -934,9 +934,6 @@ export function zip(entries: Unzipped): Uint8Array { let count = 0; for (const name in entries) { - if (count >= ZIP_UINT16_MAX || offset >= ZIP_UINT32_MAX) { - throw new ToolError("ZIP archive is too large to write (ZIP64 is not supported)"); - } const data = entries[name]!; const nameBytes = ENCODER.encode(name); const crc = zlib.crc32(data) >>> 0; @@ -946,6 +943,18 @@ export function zip(entries: Unzipped): Uint8Array { const method = stored ? ZIP_STORED_COMPRESSION : ZIP_DEFLATE_COMPRESSION; const payload = stored ? data : deflated; + // Without ZIP64 the name length is a u16 and offsets/sizes are u32 (with + // 0xffff/0xffffffff reserved as ZIP64 sentinels); reject anything that + // would silently wrap a header field instead of producing a valid archive. + if ( + count + 1 >= ZIP_UINT16_MAX || + nameBytes.byteLength > ZIP_UINT16_MAX || + uncompressedSize >= ZIP_UINT32_MAX || + offset + 30 + nameBytes.byteLength + payload.byteLength >= ZIP_UINT32_MAX + ) { + throw new ToolError("ZIP archive is too large to write (ZIP64 is not supported)"); + } + const header = new Uint8Array(30 + nameBytes.byteLength); writeUInt32LE(header, 0, ZIP_LOCAL_FILE_HEADER_SIGNATURE); writeUInt16LE(header, 4, 20); @@ -980,6 +989,9 @@ export function zip(entries: Unzipped): Uint8Array { } const centralSize = centralParts.reduce((sum, part) => sum + part.byteLength, 0); + if (centralSize >= ZIP_UINT32_MAX || offset + centralSize + ZIP_EOCD_MIN_LENGTH >= ZIP_UINT32_MAX) { + throw new ToolError("ZIP archive is too large to write (ZIP64 is not supported)"); + } const eocd = new Uint8Array(ZIP_EOCD_MIN_LENGTH); writeUInt32LE(eocd, 0, ZIP_EOCD_SIGNATURE); writeUInt16LE(eocd, 8, count);