VYPR
Moderate severityNVD Advisory· Published Mar 4, 2026· Updated Mar 5, 2026

Hono: SSE Control Field Injection via CR/LF in writeSSE()

CVE-2026-29085

Description

Hono is a Web application framework that provides support for any JavaScript runtime. Prior to version 4.12.4, when using streamSSE() in Streaming Helper, the event, id, and retry fields were not validated for carriage return (\r) or newline (\n) characters. Because the SSE protocol uses line breaks as field delimiters, this could allow injection of additional SSE fields within the same event frame if untrusted input was passed into these fields. This issue has been patched in version 4.12.4.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
hononpm
< 4.12.44.12.4

Affected products

1

Patches

1
f4123ed9ea3c

Merge commit from fork

https://github.com/honojs/honoTaku AmanoMar 3, 2026via ghsa
2 files changed · +86 0
  • src/helper/streaming/sse.test.tsx+80 0 modified
    @@ -296,6 +296,86 @@ describe('SSE Streaming helper', () => {
         expect(decodedValue).toBe('event: test-mixed\ndata: A\ndata: B\ndata: C\ndata: D\n\n')
       })
     
    +  it('Should throw error if event contains \\n', async () => {
    +    const onError = vi.fn()
    +    const res = streamSSE(
    +      c,
    +      async (stream) => {
    +        await stream.writeSSE({ data: 'test', event: 'test\nevent' })
    +      },
    +      onError
    +    )
    +    if (!res.body) {
    +      throw new Error('Body is null')
    +    }
    +    const reader = res.body.getReader()
    +    const decoder = new TextDecoder()
    +    const { value } = await reader.read()
    +    const decodedValue = decoder.decode(value)
    +    expect(decodedValue).toContain('event: error')
    +    expect(onError).toBeCalledTimes(1)
    +  })
    +
    +  it('Should throw error if event contains \\r', async () => {
    +    const onError = vi.fn()
    +    const res = streamSSE(
    +      c,
    +      async (stream) => {
    +        await stream.writeSSE({ data: 'test', event: 'test\revent' })
    +      },
    +      onError
    +    )
    +    if (!res.body) {
    +      throw new Error('Body is null')
    +    }
    +    const reader = res.body.getReader()
    +    const decoder = new TextDecoder()
    +    const { value } = await reader.read()
    +    const decodedValue = decoder.decode(value)
    +    expect(decodedValue).toContain('event: error')
    +    expect(onError).toBeCalledTimes(1)
    +  })
    +
    +  it('Should throw error if id contains \\n', async () => {
    +    const onError = vi.fn()
    +    const res = streamSSE(
    +      c,
    +      async (stream) => {
    +        await stream.writeSSE({ data: 'test', id: 'test\nid' })
    +      },
    +      onError
    +    )
    +    if (!res.body) {
    +      throw new Error('Body is null')
    +    }
    +    const reader = res.body.getReader()
    +    const decoder = new TextDecoder()
    +    const { value } = await reader.read()
    +    const decodedValue = decoder.decode(value)
    +    expect(decodedValue).toContain('event: error')
    +    expect(onError).toBeCalledTimes(1)
    +  })
    +
    +  it('Should throw error if id contains \\r', async () => {
    +    const onError = vi.fn()
    +    const res = streamSSE(
    +      c,
    +      async (stream) => {
    +        await stream.writeSSE({ data: 'test', id: 'test\rid' })
    +      },
    +      onError
    +    )
    +    if (!res.body) {
    +      throw new Error('Body is null')
    +    }
    +    const reader = res.body.getReader()
    +    const decoder = new TextDecoder()
    +    const { value } = await reader.read()
    +    const decodedValue = decoder.decode(value)
    +    expect(decodedValue).toContain('event: error')
    +    expect(onError).toBeCalledTimes(1)
    +  })
    +
       it('Check streamSSE handles consecutive \\r correctly', async () => {
         const res = streamSSE(c, async (stream) => {
           await stream.writeSSE({
    
  • src/helper/streaming/sse.ts+6 0 modified
    @@ -24,6 +24,12 @@ export class SSEStreamingApi extends StreamingApi {
           })
           .join('\n')
     
    +    for (const key of ['event', 'id', 'retry'] as (keyof SSEMessage)[]) {
    +      if (message[key] && /[\r\n]/.test(message[key] as string)) {
    +        throw new Error(`${key} must not contain "\\r" or "\\n"`)
    +      }
    +    }
    +
         const sseData =
           [
             message.event && `event: ${message.event}`,
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.