RegExpRouter: wildcard routes don't match paths containing an encoded line terminator (%0A, %0D)
What version of Hono are you using?
4.13.7
What runtime/platform is your app running on? (with version if possible)
Node.js v24.20.0 with @hono/node-server, but the reproduction below uses app.request(), so it looks runtime-independent.
What steps can reproduce the bug?
import { Hono } from 'hono'
const app = new Hono()
app.get('/*', (c) => c.text('matched'))
console.log((await app.request('/a%20b')).status) // 200
console.log((await app.request('/a%0Ab')).status) // 404 <- expected 200getPath() runs the path through decodeURI before routing, so %0A becomes a real newline. RegExpRouter compiles a wildcard to ONLY_WILDCARD_REG_EXP_STR = '.*' (and TAIL_WILDCARD_REG_EXP_STR = '(?:|/.*)'), and in JavaScript . never matches a line terminator unless the s flag is set. The route therefore cannot match and the request falls through to the not-found handler.
Named parameters are not affected, because LABEL_REG_EXP_STR = '[^/]+' does match those characters:
app.get('/:p', (c) => c.text('matched')) // '/a%0Ab' -> 200I ran into this with a route that accepts free text in a path segment: any text containing a newline 404s, while everything else works.
What is the expected behavior?
A wildcard route matches any path, including one whose decoded form contains a line terminator, the same way a named parameter already does.
What do you see instead?
A 404 from the not-found handler. The route handler is never called.
The affected characters are exactly the four JavaScript line terminators: \n (%0A), \r (%0D), \u2028 (%E2%80%A8) and \u2029 (%E2%80%A9). Tab, form feed, vertical tab and NUL all match fine.
With app.get('/*', ...) and a request to /a%0Ab:
| Router | Result |
|---|---|
| SmartRouter (default) | 404 |
| RegExpRouter | 404 |
| TrieRouter | 200 |
| PatternRouter | 200 |
| LinearRouter | 200 |
SmartRouter cannot route around this. It selects a router on the first request and only moves on when add() throws UnsupportedPathError; RegExpRouter registers the route without complaint and merely fails to match later, which is indistinguishable from a genuine 404.
Only the wildcard patterns are affected. A wildcard in the middle of a path, such as /files/*/download, compiles to LABEL_REG_EXP_STR and matches correctly.
Additional information
new Hono({ router: new TrieRouter() }) is a working workaround.
A fix might be to make the wildcard patterns match line terminators, for example '[\s\S]*' in place of '.*' and '(?:|/[\s\S]*)' in place of '(?:|/.*)', or to compile the matcher with the s flag.
Source: honojs/hono