GHSA-c6fg-446q-cg94: Medium severity npm/adm-zip vulnerability
Summary adm-zip enforces a maxOutputLength guard against decompression bombs on its synchronous getData() path, but the equivalent asynchronous getDataAsync() path does not enforce it — it accumulates and returns the entire decompressed output regardless of the entry's declared size. An application that checks an entry's declared size before deciding to process it, then reads the entry via getDataAsync() (a completely ordinary, often-recommended choice for I/O in Node.js), gets none of the protection it believes it has.
Details
- methods/inflater.js:3-10 passes {maxOutputLength: expectedLength} to inflateRawSync, which Node enforces. - methods/inflater.js:12-31 passes the same option to createInflateRaw but never enforces it on the streaming path — it just accumulates every chunk and allocates a final Buffer of whatever size resulted.
PoC
js const AdmZip = require('adm-zip'); const zip = new AdmZip(); zip.addFile('p', Buffer.alloc(64 1024 1024, 0x41)); // 64 MiB const raw = Buffer.from(zip.toBuffer()); // patch the local + central declared uncompressed size to 1 byte raw.writeUInt32LE(1, localHeaderSizeOffset); raw.writeUInt32LE(1, centralHeaderSizeOffset); const entry = new AdmZip(raw).getEntry('p');
entry.getData(); // throws: ERRBUFFERTOOLARGE: Cannot create a Buffer larger than 1 bytes
entry.getDataAsync((data, error) => { ... }); // returns the full 67,108,864-byte buffer, error is undefined
Impact An application that validates a declared size before reading an entry, then uses the async API, gets no protection against a high-ratio DEFLATE payload. Repeated or larger requests could contribute to memory exhaustion.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
npm/adm-zipto a version that resolves this vulnerability.Fixed in 0.6.1
Event History
Frequently Asked Questions
Which applications are exposed to this issue?
Applications that process attacker-controlled ZIP archives with adm-zip's asynchronous getDataAsync() path are exposed. This is especially relevant when they trust an entry's declared uncompressed size before deciding whether to extract it.
What does an attacker need to do to trigger the issue?
An attacker needs to provide a crafted ZIP entry whose declared uncompressed size is small but whose actual decompressed content is much larger. When the application reads that entry with getDataAsync(), the asynchronous path accumulates the full output.
Does using a declared-size check protect applications using getDataAsync()?
No. The asynchronous inflater passes maxOutputLength to the streaming API but does not enforce the limit while collecting decompressed chunks, so the returned Buffer can exceed the entry's declared size.
How can I identify potentially affected code?
Look for uses of adm-zip getDataAsync() on ZIP files that may be supplied by users or other untrusted sources. Code that uses ZIP metadata such as the declared uncompressed size as a processing or memory-safety limit is particularly at risk.
What should be done if the application cannot immediately change dependencies?
Avoid processing untrusted ZIP entries through getDataAsync() where possible. Apply an independently enforced limit to decompressed data rather than relying on the archive's declared size or the asynchronous path's maxOutputLength option.