Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
💧 Hex
Not in CISA KEV

GHSA-655f-mp8p-96gv

Fix: wojtekmach/req@84977e5

GHSA-655f-mp8p-96gv is a CWE-409 vulnerability in req. O3 Security confirms whether GHSA-655f-mp8p-96gv is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.

Req vulnerable to unbounded archive/compression extraction triggered by response content-type

Also known asCVE-2026-49755EEF-CVE-2026-49755
Published
Jul 29, 2026
Updated
Jul 29, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Jul 29, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Real-World Exposure

1 pkg affected
💧req

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Hex packages — download data is not available via public APIs for these ecosystems.

Description

Summary

Req's default response pipeline auto-decodes archive and compressed bodies based on the server-supplied content-type (or URL extension) and materialises the full decompressed contents in memory with no size cap. An attacker who controls (or can redirect a victim into) an HTTP endpoint reached by Req.get!/1 can return a tiny "decompression bomb" that expands to many gigabytes on the client and exhausts the BEAM's memory.

Details

1. Archive auto-decoding. Req.Steps.decode_body/1 in lib/req/steps.ex dispatches on the response content-type (or URL extension) and calls Erlang's archive libraries with :memory, returning a [{name, bytes}] list of every entry fully decompressed in RAM: application/zip:zip.extract(body, [:memory]), application/x-tar:erl_tar.extract({:binary, body}, [:memory]), application/gzip / .tgz:erl_tar.extract({:binary, body}, [:memory, :compressed]). No byte cap is enforced before decoding and no per-entry size limit is passed to :zip / :erl_tar.

2. content-encoding chaining. Req.Steps.decompress_body/1 walks the content-encoding header and chains :zlib / :brotli / :ezstd decoders, so a response advertising content-encoding: gzip, gzip, gzip, … inflates through multiple layers without bound.

3. Default-on, attacker-chosen decoder. Both steps are part of Req's default pipeline. The caller does not need to opt in, and the attacker chooses which decoder fires by setting content-type and content-encoding on their own server (or on any host reached via Req's automatic redirect following).

PoC

  1. Run an HTTP server that responds 200 with content-type: application/zip and a body that is a zip archive whose single entry is ~400 MB of zero bytes (compressed wire payload: a few hundred KB).
  2. From the victim process, call Req.get!(url) against that server (no special options, no opt-in to archive decoding).
  3. decode_body/1 dispatches on content-type, invokes :zip.extract(body, [:memory]), and the response body becomes [{~c"bomb.bin", <<400 MB of zero bytes>>}]. A sub-MB request produces hundreds of MB resident memory; layering gzip on the content-encoding path or increasing entry size scales arbitrarily.

Impact

Memory-exhaustion denial of service against any Elixir application that uses Req with its default step pipeline to fetch URLs influenced by an untrusted party, including webhook senders, link previews, OAuth/OIDC discovery clients, package mirrors, image proxies, and any Req.get!/1 call that may follow redirects to attacker-controlled hosts. No authentication is required; a single response can crash the BEAM and take down unrelated workloads on the same VM.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
💧Hexreq0.1.0&&< 0.6.10.6.1

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for req. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.

  2. Fix

    Update req to 0.6.1 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-655f-mp8p-96gv is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 pinpoints whether GHSA-655f-mp8p-96gv is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.

Tailored to GHSA-655f-mp8p-96gv. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### Summary Req's default response pipeline auto-decodes archive and compressed bodies based on the server-supplied `content-type` (or URL extension) and materialises the full decompressed contents in memory with no size cap. An attacker who controls (or can redirect a victim into) an HTTP endpoint reached by `Req.get!/1` can return a tiny "decompression bomb" that expands to many gigabytes on the client and exhausts the BEAM's memory. ### Details **1. Archive auto-decoding.** `Req.Steps.decode_body/1` in `lib/req/steps.ex` dispatches on the response `content-type` (or URL extension) and ca
O3 Security · Impact-Aware SCA

Is GHSA-655f-mp8p-96gv in your dependencies?

O3 detects GHSA-655f-mp8p-96gv across Hex dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.

GHSA-655f-mp8p-96gv: req Denial of Service | O3 Security