{"record":{"id":"ed086ee3b0bc4e12","repo":"actualbudget/actual","slug":"zip-entry-file-name-exceeds-maximum-size-of","errorCode":null,"errorMessage":"Zip entry \"${file.name}\" exceeds maximum size of ${maxEntrySize} bytes","messagePattern":"Zip entry \"(.+?)\" exceeds maximum size of (.+?) bytes","errorType":"exception","errorClass":"UnsafeZipError","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/util/zip.ts","lineNumber":67,"sourceCode":"  }: SafeUnzipOptions = {},\n): Unzipped {\n  if (data.length > maxArchiveSize) {\n    throw new UnsafeZipError(\n      `Zip archive exceeds maximum size of ${maxArchiveSize} bytes`,\n      { zipReason: 'archive-size', maxSize: maxArchiveSize },\n    );\n  }\n\n  const seen = new Set<string>();\n\n  let totalUncompressedSize = 0;\n\n  return unzipSync(data, {\n    filter(file) {\n      assertSafeEntryName(file.name);\n\n      if (file.originalSize > maxEntrySize) {\n        throw new UnsafeZipError(\n          `Zip entry \"${file.name}\" exceeds maximum size of ${maxEntrySize} bytes`,\n          {\n            zipReason: 'entry-size',\n            entryName: file.name,\n            maxSize: maxEntrySize,\n          },\n        );\n      }\n\n      totalUncompressedSize += file.originalSize;\n      if (totalUncompressedSize > maxTotalUncompressedSize) {\n        throw new UnsafeZipError(\n          `Zip archive's total uncompressed size exceeds maximum of ${maxTotalUncompressedSize} bytes`,\n          { zipReason: 'total-size', maxSize: maxTotalUncompressedSize },\n        );\n      }\n\n      const normalized = file.name.toLowerCase();","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/util/zip.ts#L49-L85","documentation":"During unzipSync's filter callback, each entry's uncompressed size (originalSize) is checked against maxEntrySize before extraction. Any single entry exceeding the limit throws UnsafeZipError with zipReason 'entry-size', naming the entry and the limit. This prevents one huge file inside the archive from exhausting memory.","triggerScenarios":"A zip containing one entry whose decompressed size exceeds maxEntrySize (default MAX_ZIP_SIZE) — a decompression bomb with a small compressed payload expanding to gigabytes, or a legitimately huge single export file.","commonSituations":"Malicious archives designed to expand massively on extraction; backups containing very large embedded attachments; importing third-party archives of unknown content.","solutions":["Inspect the named entry — if it is unexpected, treat the archive as malicious and reject it.","For legitimate large entries, raise maxEntrySize when calling safeUnzip.","Check entry sizes from the central directory before extraction in your own pre-scan and report a friendly error.","Never bypass the check on untrusted archives; process oversized entries in a worker with a hard memory cap instead."],"exampleFix":"// before\nconst entries = safeUnzip(buffer);\n// after\nconst entries = safeUnzip(buffer, {\n  maxEntrySize: 200 * 1024 * 1024, // allow bigger individual files\n});","handlingStrategy":"try-catch","validationCode":"// Read central directory sizes before extraction if available, or cap input:\nif (buf.length > MAX_ARCHIVE) throw new Error('Archive too large to inspect safely');","typeGuard":null,"tryCatchPattern":"try {\n  const entries = safeUnzip(buf);\n} catch (e) {\n  if (e instanceof UnsafeZipError && e.zipReason === 'entry-size') {\n    showError(`Entry \"${e.entryName}\" is too large (limit ${e.maxSize} bytes)`);\n  } else { throw e; }\n}","preventionTips":["Treat small-zip-huge-content ratios as a bomb indicator and reject","Configure maxEntrySize explicitly for workloads with legitimately large files","Run extraction of untrusted archives in a memory-capped worker"],"tags":["zip","security","size-limit","decompression-bomb"],"backgroundTag":"decompression-bomb","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}