Skip to content

eventsource-encoder vulnerable to SSE event injection via unsanitized `event` and `id` fields

Moderate severity GitHub Reviewed Published May 5, 2026 in rexxars/eventsource-encoder • Updated Jun 8, 2026

Package

npm eventsource-encoder (npm)

Affected versions

<= 1.0.1

Patched versions

1.0.2

Description

Summary

eventsource-encoder does not sanitize the event or id fields of an EventSourceMessage before serializing them. An attacker who controls either field can inject arbitrary Server-Sent Events line terminators (\n, \r, or \r\n) and thereby forge additional SSE fields or entire messages on the stream. This is similar in spirit to GHSA-4hxc-9384-m385 (h3), but the vulnerable fields are event/id rather than data/comment. These are less likely to be user-controllable, but should still be sanitized.

Details

In src/encode.ts, encodeMessage interpolates event and id into the output without inspecting them for line terminators:

if (message.event) {
  output += `event: ${message.event}\n`
}
// ...
if (typeof message.id === 'string' || typeof message.id === 'number') {
  output += `id: ${message.id}\n`
}

The SSE specification treats \r, \n, and \r\n as line terminators. A \n (or \r) embedded in either field is rendered as the end of that field, allowing the rest of the input to be interpreted by the client as new SSE fields.

By contrast, data and comment already normalize all three line-terminator forms via NEWLINES_RE = /(\r\n|\r|\n)/g, so they are not affected.

Proof of concept

import {encode} from 'eventsource-encoder'

// Attacker-controlled value flows into `event`
const userSuppliedTopic = 'message\nevent: admin\ndata: {"role":"admin"}'

console.log(encode({event: userSuppliedTopic, data: 'hello'}))

Output:

event: message
event: admin
data: {"role":"admin"}
data: hello

The browser sees two events: a forged admin event with attacker-chosen payload, followed by the legitimate message event. The same primitive works through id for any string id value.

Impact

If untrusted input is passed into the event or id field of a message, an attacker can:

  • Spoof events of arbitrary type (rerouting payloads to handlers the attacker chooses)
  • Inject additional SSE fields (data:, id:, retry:) into the stream
  • Split a single encode() call into multiple distinct browser events
  • Override the client's Last-Event-ID via injected id: lines

The vulnerability requires that an application places attacker-controlled data into event or id. Applications that only put trusted, statically-defined values into these fields are not affected.

Patches

Fixed in eventsource-encoder@1.0.2. The event and string id fields are now validated; any value containing \r or \n causes the encoder to throw a TypeError rather than emit a malformed stream.

Workarounds

If users cannot upgrade, validate or strip line terminators from any untrusted value before passing it to encode / encodeMessage:

function safeSingleLine(value) {
  if (/[\r\n]/.test(value)) throw new Error('SSE field must be single-line')
  return value
}

encode({event: safeSingleLine(topic), id: safeSingleLine(id), data})

Resources

Credit

Discovered while reviewing in light of GHSA-4hxc-9384-m385.

References

@rexxars rexxars published to rexxars/eventsource-encoder May 5, 2026
Published to the GitHub Advisory Database May 8, 2026
Reviewed May 8, 2026
Published by the National Vulnerability Database May 26, 2026
Last updated Jun 8, 2026

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Changed
Confidentiality
None
Integrity
Low
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:N

EPSS score

Exploit Prediction Scoring System (EPSS)

This score estimates the probability of this vulnerability being exploited within the next 30 days. Data provided by FIRST.
(20th percentile)

Weaknesses

Improper Neutralization of CRLF Sequences ('CRLF Injection')

The product uses CRLF (carriage return line feeds) as a special element, e.g. to separate lines or records, but it does not neutralize or incorrectly neutralizes CRLF sequences from inputs. Learn more on MITRE.

Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Request/Response Splitting')

The product receives data from an HTTP agent/component (e.g., web server, proxy, browser, etc.), but it does not neutralize or incorrectly neutralizes CR and LF characters before the data is included in outgoing HTTP headers. Learn more on MITRE.

CVE ID

CVE-2026-44214

GHSA ID

GHSA-m9g3-3g99-mhpx
Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.