CVE-2024-32652 (published 2024-04-19T19:15Z)

The adapter @hono/node-server allows you to run your Hono application on Node.js. Prior to 1.10.1, the application hangs when receiving a Host header with a value that `@hono/node-server` can't handle well. Invalid values are those that cannot be parsed by the `URL` as a hostname such as an empty string, slashes `/`, and other strings. The version 1.10.1 includes the fix for this issue.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/honojs/node-server
commit_details: repo_url: https://github.com/honojs/node-server
author: Taku Amano
author_date: 2024-04-19 14:48:36+09:00
msg: fix: catch ERR_INVALID_URL error in listener (#162)
repository: repo_url: https://github.com/honojs/node-server
repo_name: honojs/node-server
description: Node.js Server for Hono
repo_language: TypeScript
owner: honojs
file_changes: filename: listener.ts
change_type: ModificationType.MODIFY
diff: @@ -159,18 +159,18 @@ export const getRequestListener = (
   ) => {
     let res
 
-    // `fetchCallback()` requests a Request object, but global.Request is expensive to generate,
-    // so generate a pseudo Request object with only the minimum required information.
-    const req = newRequest(incoming)
-
-    // Detect if request was aborted.
-    outgoing.on('close', () => {
-      if (incoming.destroyed) {
-        req[getAbortController]().abort()
-      }
-    })
-
     try {
+      // `fetchCallback()` requests a Request object, but global.Request is expensive to generate,
+      // so generate a pseudo Request object with only the minimum required information.
+      const req = newRequest(incoming)
+
+      // Detect if request was aborted.
+      outgoing.on('close', () => {
+        if (incoming.destroyed) {
+          req[getAbortController]().abort()
+        }
+      })
+
       res = fetchCallback(req, { incoming, outgoing } as HttpBindings) as
         | Response
         | Promise<Response>

programming_language: TypeScript
filename: listener.test.ts
change_type: ModificationType.MODIFY
diff: @@ -4,6 +4,28 @@ import { getRequestListener } from '../src/listener'
 import { GlobalRequest, Request as LightweightRequest } from '../src/request'
 import { GlobalResponse, Response as LightweightResponse } from '../src/response'
 
+describe('Invalid request', () => {
+  const requestListener = getRequestListener(jest.fn())
+  const server = createServer(async (req, res) => {
+    await requestListener(req, res)
+
+    if (!res.writableEnded) {
+      res.writeHead(500, { 'Content-Type': 'text/plain' })
+      res.end('error handler did not return a response')
+    }
+  })
+
+  it('Should return server error for a request w/o host header', async () => {
+    const res = await request(server).get('/').set('Host', '').send()
+    expect(res.status).toBe(500)
+  })
+
+  it('Should return server error for a request invalid host header', async () => {
+    const res = await request(server).get('/').set('Host', 'a b').send()
+    expect(res.status).toBe(500)
+  })
+})
+
 describe('Error handling - sync fetchCallback', () => {
   const fetchCallback = jest.fn(() => {
     throw new Error('thrown error')

programming_language: TypeScript