{"record":{"id":"c07a8010bec747aa","repo":"vercel-labs/skills","slug":"invalid-zip-archive-label-is-out-of-bounds","errorCode":null,"errorMessage":"Invalid zip archive: ${label} is out of bounds","messagePattern":"Invalid zip archive: (.+?) is out of bounds","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/archive.ts","lineNumber":41,"sourceCode":"  maxEntries: number;\n}\n\nexport class ArchiveValidationError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = 'ArchiveValidationError';\n  }\n}\n\nfunction ensureRange(buffer: Buffer, offset: number, length: number, label: string): void {\n  if (\n    !Number.isSafeInteger(offset) ||\n    !Number.isSafeInteger(length) ||\n    offset < 0 ||\n    length < 0 ||\n    offset + length > buffer.length\n  ) {\n    throw new Error(`Invalid zip archive: ${label} is out of bounds`);\n  }\n}\n\nfunction findEndOfCentralDirectory(buffer: Buffer): number {\n  const minOffset = Math.max(0, buffer.length - ZIP_MAX_COMMENT_SIZE - ZIP_END_MIN_SIZE);\n  for (let offset = buffer.length - ZIP_END_MIN_SIZE; offset >= minOffset; offset--) {\n    if (buffer.readUInt32LE(offset) !== ZIP_END_OF_CENTRAL_DIRECTORY) continue;\n\n    const commentLength = buffer.readUInt16LE(offset + 20);\n    if (offset + ZIP_END_MIN_SIZE + commentLength === buffer.length) {\n      return offset;\n    }\n  }\n  return -1;\n}\n\nfunction readUInt64AsNumber(buffer: Buffer, offset: number, label: string): number {\n  ensureRange(buffer, offset, 8, label);","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/vercel-labs/skills/blob/435076e78988e1e6ec40d00b0b1d76bdbbc5419a/src/archive.ts#L23-L59","documentation":"This error is thrown by the zip parser's internal bounds checker, ensureRange, whenever a computed offset/length pair falls outside the archive buffer. It means the parser located a structure (central directory, extra field, zip64 record) whose declared position or size points past the end (or before the start) of the Buffer being parsed. It almost always indicates a truncated or corrupt zip file rather than a misuse of the API.","triggerScenarios":"Passing a truncated zip Buffer (e.g. an incomplete download or partial stream write) to readZipArchive; a central directory offset field that was corrupted and points beyond buffer.length; a zip64 record whose declared size (recordSize + 12) overruns the buffer; an extra field whose length extends past its record.","commonSituations":"Downloading a zip over an interrupted HTTP stream and parsing before completion; reading a file concurrently while it is still being written; a git-lfs or cloud-storage placeholder file that is smaller than the real artifact; hand-crafted or fuzzed zip test fixtures with bogus offsets.","solutions":["Verify the file transfer completed before parsing (compare Content-Length to bytes received, or re-download and retry).","Check the file is a real zip: run `unzip -t file.zip` or check the magic bytes PK\\x03\\x04 before handing it to the library.","Ensure you read the entire file into the Buffer (no partial fs.read / stream truncation) and that nothing mutated the buffer afterwards.","If the input is user-supplied, validate its integrity (CRC/checksum or size against a manifest) before parsing."],"exampleFix":"// before\nconst buffer = await fs.promises.readFile(partialPath);\nconst zip = readZipArchive(buffer); // may throw 'out of bounds'\n\n// after\nconst buffer = await fs.promises.readFile(partialPath);\nif (buffer.subarray(0, 2).toString('binary') !== 'PK') {\n  throw new Error('Not a zip file');\n}\nif (bytesReceived !== expectedSize) {\n  throw new Error('Incomplete download');\n}\nconst zip = readZipArchive(buffer);","handlingStrategy":"validation","validationCode":"function looksLikeCompleteZip(buffer: Buffer): boolean {\n  return (\n    buffer.length >= 22 &&\n    buffer.subarray(0, 2).toString('binary') === 'PK' &&\n    buffer.lastIndexOf(Buffer.from([0x50, 0x4b, 0x05, 0x06])) !== -1 // EOCD present\n  );\n}","typeGuard":"null","tryCatchPattern":"try {\n  const zip = readZipArchive(buffer);\n} catch (e) {\n  if ((e as Error).message.includes('out of bounds')) {\n    // treat as truncated/corrupt input: reject the file or re-fetch\n  }\n  throw e;\n}","preventionTips":["Download to a temp file and rename only after size/checksum validation.","Check magic bytes PK and EOCD signature presence before parsing.","Never parse a file that another process may still be writing."],"tags":["zip","archive","corrupt-file","bounds-check","parsing"],"backgroundTag":"corrupt-archive-file","analyzedSha":"435076e78988e1e6ec40d00b0b1d76bdbbc5419a","analyzedAt":"2026-08-28T17:47:53.369Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}