{"record":{"id":"a48bb8b13e84e3b0","repo":"actualbudget/actual","slug":"zip-archive-exceeds-maximum-size-of-maxarchivesi","errorCode":null,"errorMessage":"Zip archive exceeds maximum size of ${maxArchiveSize} bytes","messagePattern":"Zip archive exceeds maximum size of (.+?) bytes","errorType":"exception","errorClass":"UnsafeZipError","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/util/zip.ts","lineNumber":52,"sourceCode":"  }\n}\n\ntype SafeUnzipOptions = {\n  maxArchiveSize?: number;\n  maxEntrySize?: number;\n  maxTotalUncompressedSize?: number;\n};\n\nexport function safeUnzip(\n  data: Uint8Array,\n  {\n    maxArchiveSize = MAX_ZIP_SIZE,\n    maxEntrySize = MAX_ZIP_SIZE,\n    maxTotalUncompressedSize = MAX_ZIP_SIZE,\n  }: 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',","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/util/zip.ts#L34-L70","documentation":"safeUnzip enforces a maximum compressed archive size before decompressing to prevent decompression-bomb / resource-exhaustion attacks. If the raw buffer exceeds maxArchiveSize (default MAX_ZIP_SIZE), it throws UnsafeZipError with zipReason 'archive-size' and the configured limit.","triggerScenarios":"Importing or loading a backup zip larger than the configured limit — a legitimately huge budget backup, or an intentionally oversized malicious upload. Callers include loadBackup, importBuffer, and parseFile.","commonSituations":"Users importing very large backup archives containing many attachments; a server receiving oversized uploads without an HTTP-level body size limit; misconfigured smaller maxArchiveSize rejecting valid archives.","solutions":["Check the file size client-side before upload/import and reject archives over the limit early with a clear message.","If legitimate larger backups are expected, pass a higher maxArchiveSize option to safeUnzip.","Enforce an HTTP request body limit at the server/proxy layer so oversized uploads fail fast.","Split or prune the backup (remove attachments) to reduce archive size."],"exampleFix":"// before\nconst entries = safeUnzip(buffer); // default 25MB-ish limit\n// after\nconst MAX = 100 * 1024 * 1024;\nif (buffer.length > MAX) {\n  throw new Error(`Backup too large (${buffer.length} bytes); limit is ${MAX}`);\n}\nconst entries = safeUnzip(buffer, { maxArchiveSize: MAX });","handlingStrategy":"validation","validationCode":"const MAX_ARCHIVE = 25 * 1024 * 1024;\nif (data.length > MAX_ARCHIVE) {\n  throw new Error(`Archive is ${(data.length / 1e6).toFixed(1)}MB; limit is ${MAX_ARCHIVE / 1e6}MB`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const entries = safeUnzip(buf);\n} catch (e) {\n  if (e instanceof UnsafeZipError && e.zipReason === 'archive-size') {\n    showError(`Backup too large — limit is ${e.maxSize} bytes`);\n  } else { throw e; }\n}","preventionTips":["Check file size before upload/import and fail fast with a clear message","Enforce an HTTP body-size limit at the proxy/server layer","Raise maxArchiveSize explicitly if legitimate backups are known to be larger"],"tags":["zip","security","size-limit","dos"],"backgroundTag":"archive-size-limit-exceeded","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}