Skip to content

Host header parsing mishandles IPv6 literals in node/express/hono/bun middleware #76

@nficano

Description

@nficano

Four middleware packages strip the port from the Host header using raw.split(":", 1)[0], but String.prototype.split with a limit returns the first N pieces from a global split on every delimiter — it is not a Python-style split-once. For an IPv6 Host header like [::1]:443, "[::1]:443".split(":", 1) returns ["["] rather than "[::1]". The resulting "host" the guard compares against allowedHosts is the single character [, so every legitimate IPv6 client is rejected and every misconfigured guard that only knows about IPv4 happens to be "right" for the wrong reason.

The affected sites are packages/middleware/express/src/index.ts:82, packages/middleware/bun/src/index.ts:161, packages/middleware/hono/src/index.ts:78, and packages/middleware/node/src/index.ts:129. Three of these are byte-for-byte identical implementations of hostHeaderGuard and so share one fix; the bun one is an inline copy. There is no test fixture exercising an IPv6 Host header — a fix should add one.

Fix prompt: replace the raw.split(":", 1)[0] ?? "" lines with a bracket-aware parser. The simplest correct form is const lastColon = raw.lastIndexOf(":"); const lastBracket = raw.lastIndexOf("]"); const host = lastColon > lastBracket ? raw.slice(0, lastColon) : raw; which leaves [::1] intact, strips the :443 from [::1]:443, and leaves example.com and example.com:443 correct as well. Extract this helper into one place (e.g. packages/middleware/node/src/host.ts) and import from the other three middlewares so the implementations stay in sync. Add a table test for the helper covering "example.com", "example.com:80", "[::1]", "[::1]:443", "[2001:db8::1]:8080", "127.0.0.1:9000", and an empty string. Add an integration test against each middleware with an allowedHosts array including "[::1]" to assert IPv6 clients are not rejected.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingseverity:highHigh severity

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions