h3 has a Server-Sent Events Injection via Unsanitized Newlines in Event Stream Fields
Description
H3 is a minimal H(TTP) framework. In versions prior to 1.15.6 and between 2.0.0 through 2.0.1-rc.14, createEventStream is vulnerable to Server-Sent Events (SSE) injection due to missing newline sanitization in formatEventStreamMessage() and formatEventStreamComment(). An attacker who controls any part of an SSE message field (id, event, data, or comment) can inject arbitrary SSE events to connected clients. This issue is fixed in versions 1.15.6 and 2.0.1-rc.15.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In h3 < 1.15.6 and 2.0.0-2.0.1-rc.14, missing newline sanitization enables attacker-controlled SSE fields to inject arbitrary events into connected clients.
Vulnerability
Overview
CVE-2026-33128 is a Server-Sent Events (SSE) injection vulnerability in the h3 minimal HTTP framework. The flaw exists in formatEventStreamMessage() and formatEventStreamComment() functions within src/utils/internal/event-stream.ts. These functions do not sanitize user-controlled fields (id, event, data, comment) for newline characters before inserting them into SSE frames. Because the SSE protocol uses \n as a field delimiter and \n\n as an event separator, an unescaped newline breaks framing and allows injection.[1][2]
Attack
Vector and Exploitation
An attacker who controls any part of an SSE message field can exploit this by introducing newline characters. For example, injecting into the event field can change the intended wire format from event: message to event: message\nevent: admin\ndata: ALL_USERS_HACKED, effectively injecting arbitrary SSE fields or entirely new events. No special authentication is required beyond the ability to influence SSE message content, making this exploitable in any application that passes untrusted input to createEventStream.[2]
Impact
Successful exploitation allows an attacker to inject arbitrary SSE events to connected clients. This can manipulate reconnection behavior via retry directives, override Last-Event-ID via id injection, or force aggressive reconnection attempts leading to a denial-of-service condition. The browser's EventSource API processes the injected events, allowing the attacker to impersonate legitimate server messages or disrupt client-server data flows.[2][3]
Mitigation
The vulnerability is fixed in h3 versions 1.15.6 and 2.0.1-rc.15. Users should upgrade immediately to these patched releases. No known workarounds exist for earlier versions, as the functions directly control SSE wire formatting without input validation. The maintainers addressed the issue by sanitizing newline characters in all event stream fields.[1][2]
AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
h3npm | >= 2.0.0, < 2.0.1-rc.15 | 2.0.1-rc.15 |
h3npm | < 1.15.6 | 1.15.6 |
Affected products
1- h3js/h3v5Range: >= 2.0.0, < 2.0.1-rc.15
Patches
17791538e15cafix(sse): sanitize newlines in event stream fields to prevent SSE injection
2 files changed · +55 −4
src/utils/internal/event-stream.ts+17 −4 modified@@ -168,24 +168,37 @@ export function isEventStream(input: unknown): input is EventStream { } export function formatEventStreamComment(comment: string): string { - return `: ${comment}\n\n`; + return ( + comment + .split("\n") + .map((l) => `: ${l}\n`) + .join("") + "\n" + ); } export function formatEventStreamMessage(message: EventStreamMessage): string { let result = ""; if (message.id) { - result += `id: ${message.id}\n`; + result += `id: ${_sanitizeSingleLine(message.id)}\n`; } if (message.event) { - result += `event: ${message.event}\n`; + result += `event: ${_sanitizeSingleLine(message.event)}\n`; } if (typeof message.retry === "number" && Number.isInteger(message.retry)) { result += `retry: ${message.retry}\n`; } - result += `data: ${message.data}\n\n`; + const data = typeof message.data === "string" ? message.data : ""; + for (const line of data.split("\n")) { + result += `data: ${line}\n`; + } + result += "\n"; return result; } +function _sanitizeSingleLine(value: string): string { + return value.replace(/[\n\r]/g, ""); +} + export function formatEventStreamMessages(messages: EventStreamMessage[]): string { let result = ""; for (const msg of messages) {
test/unit/sse.test.ts+38 −0 modified@@ -33,4 +33,42 @@ describe("sse (unit)", () => { ]); expect(result).toEqual(`data: hello world\n\nid: 1\ndata: hello world 2\n\n`); }); + + it("sanitizes newlines in event field to prevent SSE injection", () => { + const result = formatEventStreamMessage({ + event: "message\nevent: admin\ndata: INJECTED", + data: "legit", + }); + expect(result).toEqual(`event: messageevent: admindata: INJECTED\ndata: legit\n\n`); + // Newlines stripped — no separate "event: admin" line that could be parsed as a new field + expect(result.split("\n").filter((l) => l.startsWith("event:")).length).toBe(1); + }); + + it("sanitizes newlines in id field to prevent SSE injection", () => { + const result = formatEventStreamMessage({ + id: "1\ndata: INJECTED", + data: "legit", + }); + expect(result).toEqual(`id: 1data: INJECTED\ndata: legit\n\n`); + }); + + it("splits multi-line data into separate data fields", () => { + const result = formatEventStreamMessage({ + data: "line1\nline2\nline3", + }); + expect(result).toEqual(`data: line1\ndata: line2\ndata: line3\n\n`); + }); + + it("prevents data field injection of new events", () => { + const result = formatEventStreamMessage({ + data: "hi\n\nevent: system\ndata: INJECTED", + }); + // Each line becomes a separate data: field, no event injection possible + expect(result).toBe(`data: hi\ndata: \ndata: event: system\ndata: data: INJECTED\n\n`); + }); + + it("sanitizes newlines in comments", () => { + const result = formatEventStreamComment("hello\ndata: INJECTED"); + expect(result).toEqual(`: hello\n: data: INJECTED\n\n`); + }); });
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-22cc-p3c6-wpvmghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-33128ghsaADVISORY
- github.com/h3js/h3/blob/52c82e18bb643d124b8b9ec3b1f62b081f044611/src/utils/internal/event-stream.tsghsax_refsource_MISCWEB
- github.com/h3js/h3/commit/7791538e15ca22437307c06b78fa155bb73632a6ghsax_refsource_MISCWEB
- github.com/h3js/h3/security/advisories/GHSA-22cc-p3c6-wpvmghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.