CVE-2026-87776: Express compression leaks native memory until the process dies

CVE-2026-87776: Express compression leaks native memory until the process dies

How to fix CVE-2026-87776: upgrade compression to 1.8.2

2 min read486 words
Contents

Hang up while gzip is still streaming and the Node process keeps the zlib memory forever. The Express compression middleware (< 1.8.2) only tears down its compression stream from inside its own res.end() wrapper. When a client aborts mid-response, that wrapper never runs, so each aborted compressed reply leaves a live native zlib handle pinned. Repeat it and RSS climbs until the process is OOM-killed. The bug is unauthenticated and needs no app bug beyond shipping compression.

Upgrade to [email protected]. There is no application-level workaround in the advisory.

What breaks

GitHub advisory GHSA-vc2v-76pw-4v95 / CVE-2026-87776 rates this High (CVSS 7.5, AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H). Impact is availability only. npm published 1.8.2 the same day as the advisory (2026-09-11). Weekly downloads for compression sit around 30 million, so most Express and Connect stacks that enable response compression are on the blast radius.

The 1.8.2 patch does three things the GHSA blurb does not spell out. It adds a close listener on the response before onHeaders runs, so a disconnect that lands before the zlib stream exists is still recorded. It releases that stream through the destroy package rather than calling stream.destroy() alone, because the patch comment states that destroy() by itself still leaks the zlib handle on some Node.js versions. And if the response already closed before the stream was created, it destroys the late stream immediately, nulls the reference, and falls back to the raw response so later writes do not keep a doomed compressor alive. HISTORY.md also notes a case-insensitive match for the Cache-Control: no-transform skip, which is unrelated to the leak but ships in the same release.

Affected: compression npm < 1.8.2. Fixed: 1.8.2.

Who is not in scope

  • Apps that never mount compression (or an Express/Connect stack that vendors it) and let a reverse proxy or CDN handle gzip/br instead.
  • Already-patched installs on 1.8.2 or later.
  • Non-HTTP uses of zlib that do not go through this middleware.
  • Browsers and clients; this is a server-side leak on the response path.

How to check

From the app root:

npm ls compression

Any resolved version below 1.8.2 is vulnerable. In lockfiles, search for the package entry and confirm the resolved version string. If you vendor a copy (Next.js and similar have historically vendored this middleware), check that tree the same way; a framework-level workaround does not replace upgrading the package you actually require.

How to fix

Upgrade compression to the patched release.

npm install [email protected]

Pin or override transitive copies so every resolved path is 1.8.2 or newer, then restart the process. The advisory lists no workaround other than the upgrade.

What this is not

This is not remote code execution, not credential theft, and not an auth bypass. A remote client that can open requests and disconnect early can drive memory exhaustion and crash the server. Damage stops at availability.

References

Continue reading

All posts