{"id":"GHSA-7q85-xj36-vmfc","summary":"adm-zip: Uncontrolled memory allocation via the declared uncompressed size (DoS)","details":"### Summary\nadm-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.\n\n### Impact\nOn 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.\n\n### Steps to reproduce\nAttachments are not supported in the advisory form, so the 105-byte PoC (sha256 `980d34356fbb248fe527b9d0ac3eabc5c99393a374014be6199523de16709386`) is inlined as base64 in this self-contained reproducer:\n\n```js\nconst AdmZip = require('adm-zip');\n// 105-byte crafted ZIP, base64-inlined\n// sha256 980d34356fbb248fe527b9d0ac3eabc5c99393a374014be6199523de16709386\nconst b64 = \"UEsDBBQAAAAAAAAAAAAAAAAABQAAAAUAAAABAAAAYWhlbGxvUEsBAhQAFAAAAAAAAAAAAAAAAAAFAAAA4C7DaQEAAAAAAAAAAAAAAAAAAAAAAGFQSwUGAAAAAAEAAQAvAAAAJAAAAAAA\";\nconst buf = Buffer.from(b64, \"base64\");          // 105 bytes\nconst zip = new AdmZip(buf);\nzip.getEntries()[0].getData();   // commits ~1.8 GB, then throws \"ADM-ZIP: CRC32 checksum failed\"\n```\n\nThe 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.\n\n### Root cause\n`zipEntry.js` does `Buffer.alloc(\u003cdeclared uncompressed size\u003e)` before checking the declared size against the compressed size / available bytes.\n\n### Suggested fix\nValidate 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.","aliases":["CVE-2026-77301"],"modified":"2026-09-18T17:30:08.405220036Z","published":"2026-09-18T17:18:01Z","database_specific":{"severity":"HIGH","github_reviewed":true,"github_reviewed_at":"2026-09-18T17:18:01Z","nvd_published_at":null,"cwe_ids":["CWE-789"]},"references":[{"type":"WEB","url":"https://github.com/cthackers/adm-zip/security/advisories/GHSA-7q85-xj36-vmfc"},{"type":"WEB","url":"https://github.com/cthackers/adm-zip/commit/491600683dacb6cb9fe0718a0eeb9cb5eb49afa6"},{"type":"PACKAGE","url":"https://github.com/cthackers/adm-zip"},{"type":"WEB","url":"https://github.com/cthackers/adm-zip/releases/tag/v0.6.1"}],"affected":[{"package":{"name":"adm-zip","ecosystem":"npm","purl":"pkg:npm/adm-zip"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"0.6.1"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-7q85-xj36-vmfc/GHSA-7q85-xj36-vmfc.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"}]}