chatboxai/chatbox · error

ZIP entry compression ratio is unsafe: ${entry.name}

Error message

ZIP entry compression ratio is unsafe: ${entry.name}

What it means

Per-entry zip-bomb guard: thrown when the entry's declared uncompressed size exceeds 1 MiB AND exceeds Math.max(1, entry.size) * entryLimits.maxCompressionRatio (default 2000x). It catches a small compressed payload that would balloon on inflation, using the header sizes so it fires before the data callback streams much content.

Source

Thrown at src/renderer/packages/backup/zip.ts:238

  const unzip = new Unzip((entry) => {
    try {
      throwIfAborted(options.signal)
      assertSafeArchivePath(entry.name)
      if (seenPaths.has(entry.name)) throw new Error(`Duplicate ZIP entry: ${entry.name}`)
      seenPaths.add(entry.name)
      entryCount++
      if (entryCount > limits.maxEntries) throw new Error('ZIP contains too many entries')
      const entryLimits = { ...limits, ...options.entryLimits?.(entry.name) }
      if (entry.originalSize !== undefined && entry.originalSize > entryLimits.maxEntryUncompressedBytes) {
        throw new Error(`ZIP entry is too large: ${entry.name}`)
      }
      if (
        entry.size !== undefined &&
        entry.originalSize !== undefined &&
        entry.originalSize > 1024 * 1024 &&
        entry.originalSize > Math.max(1, entry.size) * entryLimits.maxCompressionRatio
      ) {
        throw new Error(`ZIP entry compression ratio is unsafe: ${entry.name}`)
      }

      const chunks: Uint8Array[] = []
      let entryBytes = 0
      entry.ondata = (error, data, final) => {
        if (fatalError) return
        if (error) {
          fatalError = error
          return
        }
        entryBytes += data.length
        totalUncompressedBytes += data.length
        if (entryBytes > entryLimits.maxEntryUncompressedBytes) {
          fatalError = new Error(`ZIP entry is too large: ${entry.name}`)
          entry.terminate()
          return
        }
        if (totalUncompressedBytes > limits.maxTotalUncompressedBytes) {

View on GitHub (pinned to 81571269ad)

Solutions

  1. If high-ratio entries are legitimate for trusted paths, raise maxCompressionRatio via limits or entryLimits for those paths.
  2. Reject the archive for untrusted imports; a >2000x ratio is almost always pathological.
  3. Re-export without pathological padding/repetition on the producer side.

Example fix

// before
await readZipFileEntries(file, onEntry)

// after: raise the ratio cap for a trusted, highly-compressible log path
await readZipFileEntries(file, onEntry, {
  entryLimits: (path) =>
    path.endsWith('.log') ? { maxCompressionRatio: 10_000 } : {}
})
Defensive patterns

Strategy: validation

Validate before calling

// Raise maxCompressionRatio for trusted, highly-compressible paths only.
await readZipFileEntries(file, onEntry, {
  entryLimits: (path) =>
    path.endsWith('.log') ? { maxCompressionRatio: 10_000 } : {},
})

Try / catch

try {
  await readZipFileEntries(file, onEntry, opts)
} catch (error) {
  if (error instanceof Error && error.message.startsWith('ZIP entry compression ratio is unsafe')) {
    // For untrusted sources this is a zip-bomb: reject. For trusted logs, raise the cap.
  } else throw error
}

Prevention

When it happens

Trigger: A highly compressible entry (e.g. repeated bytes) whose compressed size is tiny relative to its uncompressed size, or a deliberately crafted zip-bomb entry.

Common situations: Test/security zip-bombs, log files full of repetition, or an exporter that compressed a sparse/padding-heavy blob.

Related errors


AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12). Data as JSON: /api/errors/c3404f3f3222cd81. Report an issue: GitHub.