GitHub Advisoryadvisory · npm
GHSA-7q85-xj36-vmfc adm-zip: Uncontrolled memory allocation via the declared uncompressed size (DoS)
HIGH
Official page ↗
- Aliases
- CVE-2026-77301
- Ecosystem
- npm
- Packages
- adm-zip
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Weaknesses
- CWE-789
- Reviewed
- 2026-09-18
Affected packages
• npm adm-zip: < 0.6.1 → fixed in 0.6.1
Description
Summary
adm-zip allocates an entry's output buffer from the declared uncompressed size (central-directory `size` field) before validating it against the actual data. A tiny crafted ZIP that declares a huge uncompressed size forces a multi-gigabyte allocation from a few bytes.
Impact
On adm-zip 0.5.17 (latest), Node 24, a 105-byte ZIP with one stored entry declaring size = 1,774,399,200 makes `new AdmZip(buf).getEntries()[0].getData()` commit ~1.8 GB of resident memory in ~4.4 s before throwing `Error: ADM-ZIP: CRC32 checksum failed`, roughly 16 million times the input size. Because the buffer is committed before any validation, on a memory-constrained host (containers, serverless, small VMs) the allocation OOM-kills the process before the CRC check (uncatchable), and concurrent requests can exhaust memory even on larger hosts. Any service that reads entries from untrusted ZIPs is exposed to a remote denial of service.
Steps to reproduce
Attachments are not supported in the advisory form, so the 105-byte PoC (sha256 `980d34356fbb248fe527b9d0ac3eabc5c99393a374014be6199523de16709386`) is inlined as base64 in this self-contained reproducer:
```js
const AdmZip = require('adm-zip');
// 105-byte crafted ZIP, base64-inlined
// sha256 980d34356fbb248fe527b9d0ac3eabc5c99393a374014be6199523de16709386
const b64 = "UEsDBBQAAAAAAAAAAAAAAAAABQAAAAUAAAABAAAAYWhlbGxvUEsBAhQAFAAAAAAAAAAAAAAAAAAFAAAA4C7DaQEAAAAAAAAAAAAAAAAAAAAAAGFQSwUGAAAAAAEAAQAvAAAAJAAAAAAA";
const buf = Buffer.from(b64, "base64"); // 105 bytes
const zip = new AdmZip(buf);
zip.getEntries()[0].getData(); // commits ~1.8 GB, then throws "ADM-ZIP: CRC32 checksum failed"
```
The single entry declares uncompressed size = 1,774,399,200 with a compressed size of 5. `getData()` allocates the full declared size before the CRC check runs, so the memory is committed regardless of the (tiny) actual payload.
Root cause
`zipEntry.js` does `Buffer.alloc()` before checking the declared size against the compressed size / available bytes.
Suggested fix
Validate the declared uncompressed size against the compressed size and a configurable maximum before allocating (yauzl, for example, requires the caller to bound this); reject or stream when the declared size is implausible relative to the input. Happy to send a patch.
Related
Alias · CVE (1)
- CVE-2026-77301 adm-zip is a JavaScript library for creating and extracting ZIP archives in Node.js. vulnerability
Weakness · CWE (1)
- CWE-789 Memory Allocation with Excessive Size Value weakness