Nov 13, 2026·13 min read·3 visits
Hono versions 4.1.4 through 4.13.15 contain flaws in `RegExpRouter` that cause wildcard middleware to be skipped when mixing explicit regex parameter syntax with shorthand syntax, combining `/path/*` middleware with `/path*` routes, or processing request paths containing encoded line terminators (`%0A`, `%0D`, `%E2%80%A8`, `%E2%80%A9`). Upgrading to Hono 4.13.16 resolves all three bypass vectors.
An incorrect authorization vulnerability (CWE-863) exists in the Hono web application framework (npm package `hono`) from version 4.1.4 prior to 4.13.16. Flaws in the registration-time coverage calculation and regular expression compilation within `RegExpRouter` cause wildcard middleware to be omitted under three specific routing conditions: parameter pattern equivalence mismatches, wildcard boundary collisions, and request paths containing percent-encoded line terminators. Because Hono's default `SmartRouter` selects `RegExpRouter` for most standard web applications, unauthenticated remote clients can bypass authentication, authorization, or security middleware registered on wildcard paths to directly access protected route handlers or the application's not-found handler.
Hono is a lightweight web application framework designed to run across any JavaScript runtime, including Cloudflare Workers, Fastly Compute, Deno, Bun, and Node.js. A core feature of Hono is its pluggable routing architecture, which exposes multiple internal router implementations (RegExpRouter, TrieRouter, LinearRouter, and PatternRouter). By default, Hono initializes applications using SmartRouter, a meta-router that analyzes registered route patterns at startup and selects the fastest compatible router engine.
For the majority of standard web applications, SmartRouter delegates routing to RegExpRouter. Unlike dynamic tree-traversal routers that evaluate path segments sequentially on every incoming HTTP request, RegExpRouter pre-compiles the entire routing table—including both endpoint handlers and middleware chains—into a single unified regular expression during route registration. While this architecture minimizes per-request lookup overhead, it relies entirely on registration-time static analysis to determine which wildcard middleware handlers apply to which specific endpoint routes.
In Hono versions 4.1.4 through 4.13.15, defects in RegExpRouter's coverage calculation and regular expression generation cause wildcard middleware to be omitted from compiled execution chains. Classified under CWE-863 (Incorrect Authorization), this vulnerability manifests across three distinct scenarios:
/api/:tenant{[^/]+}/admin/*) is not attached to routes registered with shorthand parameter syntax (e.g., /api/:tenant/admin/:id)./admin/*) is not applied to routes registered with an unslashed wildcard (e.g., /admin*) for request paths beneath /admin/.. meta-character (.*) fail to match JavaScript line terminator characters (\n, \r, \u2028, \u2029), allowing requests with percent-encoded line breaks to bypass global or prefix wildcard middleware and reach the not-found (404) handler directly.Because developers frequently attach authentication, authorization, rate-limiting, and tenant-isolation checks to wildcard paths via app.use(), omitting these handlers allows unauthenticated remote attackers to invoke protected endpoint handlers or not-found handlers without executing security controls. Applications explicitly configured to use TrieRouter, LinearRouter, or PatternRouter are unaffected, as those engines evaluate middleware coverage using mechanisms that do not suffer from these specific compilation flaws.
The root cause of CVE-2026-126358 lies in how RegExpRouter links wildcard middleware to route handlers during the registration phase (buildAllRoutes), combined with how it translates wildcard tokens into regular expression syntax.
In Hono's routing syntax, a bare named parameter such as :tenant is syntactic sugar for a parameter constrained to match any non-slash character sequence: :tenant{[^/]+}. When RegExpRouter registers paths into its internal trie, both representations eventually compile to the same capturing group expression ([^/]+).
Prior to version 4.13.16, RegExpRouter maintained a separate secondary cache of middleware paths and used a helper method (findMiddleware alongside buildWildcardRegExp) to determine whether a registered middleware path covered a given target route. This lookup operated on raw, unnormalized path strings rather than structural trie tokens. When an application registered middleware on /api/:tenant{[^/]+}/admin/* and a route handler on /api/:tenant/admin/:id, the string-based matcher compared the literal characters :tenant{[^/]+} against :tenant. Because the string representations differed, the coverage check returned false, and the middleware handler was excluded from the compiled handler list for /api/:tenant/admin/:id.
When multiple wildcard patterns shared a common prefix—specifically when middleware was registered on a slash-bounded tail wildcard (/admin/*) and a route handler was registered on an unslashed wildcard (/admin*)—RegExpRouter sorted paths by string length and attempted to link covering middleware via findMiddleware.
Because /admin* (7 characters) is shorter than /admin/* (8 characters), and because buildWildcardRegExp constructed an intermediate matcher designed to match static registration patterns rather than evaluating overlapping runtime path subsets, /admin/* was not recognized as covering the /admin/ sub-branch of /admin*. When a client requested /admin/dashboard, the compiled regular expression matched the /admin* route node directly, which had been compiled without the /admin/* middleware attached.
In JavaScript regular expressions, the dot meta-character (.) matches any UTF-16 code unit except the four standard line terminators: Line Feed (\n, U+000A), Carriage Return (\r, U+000D), Line Separator (U+2028), and Paragraph Separator (U+2029), unless the s (dotAll) flag is enabled on the RegExp instance.
In src/router/reg-exp-router/node.ts, RegExpRouter defined wildcard constants using . without enabling the s flag on the final compiled router regular expression (as adding global flags could alter the behavior of user-defined custom parameter regular expressions):
export const ONLY_WILDCARD_REG_EXP_STR = '.*'
export const TAIL_WILDCARD_REG_EXP_STR = '(?:|/.*)'When Hono receives an HTTP request containing percent-encoded line terminators in the URL path (such as /%0A or /api/v1/users%0D), the framework decodes the URI path prior to matching it against the router's regular expression. Because the decoded path contains a literal newline character, the compiled .* wildcard fails to match across the line break. Consequently, any middleware registered on * or /api/* fails to match the request, causing execution to fall through directly to app.notFound() without executing global middleware.
The vulnerability was remediated in commit 8a103aa59bcfbffd5396b1548a0dff3688777c36 by refactoring RegExpRouter across three core files: src/router/reg-exp-router/node.ts, src/router/reg-exp-router/trie.ts, and src/router/reg-exp-router/router.ts.
node.ts & trie.ts)To ensure wildcards match line terminators without modifying global RegExp flags (which would unintentionally affect user-supplied parameter patterns like :id{.+}), the maintainers replaced . with the complemented empty character class [^]. In ECMAScript regular expressions, [^] matches any character, including all line terminators, regardless of flags.
--- a/src/router/reg-exp-router/node.ts
+++ b/src/router/reg-exp-router/node.ts
@@ -3,8 +3,9 @@
export const PATH_ERROR = Symbol()
-export const ONLY_WILDCARD_REG_EXP_STR = '.*'
-export const TAIL_WILDCARD_REG_EXP_STR = '(?:|/.*)'
+// Match line terminators without changing the flags of user-defined parameter patterns.
+export const ONLY_WILDCARD_REG_EXP_STR = '[^]*'
+export const TAIL_WILDCARD_REG_EXP_STR = '(?:|/[^]*)'Additionally, inside src/router/reg-exp-router/trie.ts, the tokenizer regex used during route insertion was updated with the s (dotAll) flag so that literal line terminators inside route definitions are tokenized properly:
--- a/src/router/reg-exp-router/trie.ts
+++ b/src/router/reg-exp-router/trie.ts
@@ -64,7 +64,7 @@ export class Trie {
// ...
- const tokens = markedPath.match(/(?::[^\/]+)|(?:\/\*$)|./g) || []
+ const tokens = markedPath.match(/(?::[^\/]+)|(?:\/\*$)|./gs) || []router.ts)To resolve both the parameter equivalence flaw and the wildcard boundary collision flaw, the patch completely removes the fragile string-based findMiddleware and buildWildcardRegExp methods. Instead, the router tracks the sequence of normalized trie tokens (string[]) generated during trie.insert() and introduces a deterministic structural comparison function named covers():
// Whether `wildcard`, a branch ending with a wildcard, matches every request `target` matches:
// `wildcard` minus its wildcard is a prefix of `target`, and a `/*` also needs a boundary there.
function covers(wildcard: string[], target: string[]): boolean {
const last = wildcard.length - 1
for (let i = 0; i < last; i++) {
if (wildcard[i] !== target[i]) {
return false
}
}
return (
wildcard[last] === ONLY_WILDCARD_REG_EXP_STR ||
target.length === last ||
target[last] === '/'
)
}Because trie.insert() normalizes both :tenant and :tenant{[^/]+} into the exact same token string (([^/]+)) when building the trie nodes, wildcard[i] !== target[i] evaluates them as identical. This eliminates the syntax equivalence discrepancy.
Furthermore, to handle the /admin/* middleware vs. /admin* route overlap, RegExpRouter now splits an unslashed wildcard route ending in ONLY_WILDCARD_REG_EXP_STR ([^]*) into a secondary sub-branch (['/', ONLY_WILDCARD_REG_EXP_STR]) whenever a sibling / transition exists on the same trie node:
// Inside router.ts buildAllRoutes():
// If a node has both a '[^]*' wildcard handler and a '/' child transition,
// clone the handler onto a synthetic '/[^]*' branch so '/admin/*' middleware covers it.
if (node.children['[^]*'] && node.children['/']) {
// Propagates handlers and recalculates covers() against the '/'-prefixed branch
}The fix addresses the root causes comprehensively rather than applying point workarounds. By moving coverage resolution from raw path strings to normalized trie token sequences, any two parameter patterns that compile to identical regular expression tokens are guaranteed to match during middleware propagation. Similarly, replacing .* with [^]* handles all four ECMAScript line terminator characters (\n, \r, \u2028, \u2029) natively within the regex engine without side effects on custom user regexes.
Exploiting CVE-2026-126358 requires no authentication or special privileges, only network access to send crafted HTTP requests to an affected Hono application. Below is a technical breakdown of the three exploitation vectors and their corresponding proof-of-concept setups.
This vector applies to Hono versions 4.13.0 through 4.13.15 when an application defines an explicit default regex on a middleware parameter (or vice versa) while using standard parameter syntax on the target route.
import { Hono } from 'hono'
const app = new Hono()
// Security middleware using explicit default parameter pattern
app.use('/api/:tenant{[^/]+}/admin/*', async (c, next) => {
const apiKey = c.req.header('X-API-Key')
if (apiKey !== 'secret-admin-key') {
return c.json({ error: 'Unauthorized' }, 401)
}
await next()
})
// Target route using shorthand parameter syntax
app.get('/api/:tenant/admin/:id', (c) => {
return c.json({
tenant: c.req.param('tenant'),
adminId: c.req.param('id'),
status: 'compromised'
})
})An attacker sends a standard unauthenticated HTTP GET request:
GET /api/acme-corp/admin/42 HTTP/1.1
Host: target.example.comOn vulnerable versions, RegExpRouter fails to link the app.use() handler to the app.get() handler. The server responds with 200 OK and returns the sensitive JSON payload without checking the X-API-Key header.
This vector applies to Hono versions 4.1.4 through 4.13.15 when middleware is registered on /prefix/* and a route handler is registered on /prefix*.
import { Hono } from 'hono'
const app = new Hono()
// Access control middleware on slash-wildcard
app.use('/admin/*', async (c, next) => {
return c.text('Forbidden', 403)
})
// Catch-all handler on unslashed wildcard
app.all('/admin*', (c) => {
return c.text(`Admin panel accessed via ${c.req.path}`, 200)
})An attacker sends an HTTP request targeting a sub-path beneath /admin/:
GET /admin/users/export HTTP/1.1
Host: target.example.comInstead of returning 403 Forbidden, the router dispatches the request directly to the /admin* handler, returning 200 OK.
This vector applies to Hono versions 4.4.0 through 4.13.15 when applications rely on wildcard middleware (such as app.use('*', ...) or app.use('/api/*', ...)) to enforce authentication, IP allowlisting, security headers, or request logging prior to reaching a custom app.notFound() handler or fallback logic.
import { Hono } from 'hono'
const app = new Hono()
// Global authentication gate
app.use('*', async (c, next) => {
if (!c.req.header('Authorization')) {
return c.text('Authentication Required', 401)
}
await next()
})
// Custom notFound handler that leaks internal state or performs fallback routing
app.notFound((c) => {
return c.text(`Internal 404 Debugger: Path ${c.req.path} not found in tenant DB`, 404)
})An attacker injects a percent-encoded Line Feed (%0A), Carriage Return (%0D), Line Separator (%E2%80%A8), or Paragraph Separator (%E2%80%A9) into the request URI:
GET /private%0Aresource HTTP/1.1
Host: target.example.comWhen Hono decodes /private%0Aresource to /private\nresource, the * middleware regex (^.*$) fails to match. Rather than rejecting the request with 401 Authentication Required, Hono skips the global middleware entirely and invokes app.notFound(), exposing the internal 404 handler to unauthenticated clients.
CVE-2026-126358 is scored at 5.3 (Medium) under CVSS v3.1 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N). The Attack Vector is Network (AV:N) with Low Attack Complexity (AC:L), requiring No Privileges (PR:N) and No User Interaction (UI:N).
While the base CVSS vector assigns a Low Confidentiality impact (C:L) and No Integrity or Availability impact (I:N/A:N) based on the baseline framework behavior (such as reaching a not-found handler or reading unprotected GET routes), the effective impact in downstream applications depends heavily on what functionality is guarded by the omitted wildcard middleware.
In applications that match Vector 1 (parameter pattern mismatch) or Vector 2 (wildcard boundary collision), an unauthenticated attacker can achieve full authentication and authorization bypass on state-changing endpoints (POST, PUT, DELETE) if those routes are registered under the affected pattern pairs. In such deployments, the practical impact escalates to unauthorized data modification or administrative takeover.
In applications affected by Vector 3 (encoded line terminator bypass), attackers cannot reach standard static routes (unless those routes also match across newlines), but they can reliably bypass global middleware (app.use('*')) to trigger app.notFound(). This allows attackers to evade request logging, rate limiting, WAF middleware, or authentication gates that protect custom 404 handlers, SSR fallback renderers, or proxy pass-through logic implemented inside notFound callbacks.
According to Exploit Prediction Scoring System (EPSS) telemetry, CVE-2026-126358 holds an EPSS score of 0.00347 (0.35%, ~28.76th percentile), indicating a low probability of widespread automated exploitation. The vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, as exploiting Vectors 1 and 2 requires specific routing topology definitions in target application code.
The definitive fix for all three bypass vectors is available in Hono version 4.13.16 and later. Development teams should update the hono dependency in their project manifest (package.json or deno.json):
# npm
npm install hono@^4.13.16
# pnpm
pnpm update hono
# yarn
yarn upgrade hono
# bun
bun update hono> [!NOTE]
> After upgrading, verify the installed version in your lockfile (package-lock.json, pnpm-lock.yaml, yarn.lock, or bun.lockb) to ensure transitive dependencies or workspace overrides do not pin an older version between 4.1.4 and 4.13.15.
If immediate upgrading to 4.13.16 is blocked by deployment freezes or compatibility constraints, engineering teams can apply the following mitigations:
Override the Router Engine: Because the flaws are isolated to RegExpRouter, explicitly instantiating Hono with TrieRouter or LinearRouter eliminates all three bypass vectors immediately:
import { Hono } from 'hono'
import { TrieRouter } from 'hono/router/trie-router'
const app = new Hono({ router: new TrieRouter() })Audit and Standardize Route Patterns: Inspect all app.use(), app.get(), app.post(), and app.all() declarations across the codebase:
:param{[^/]+}) with standard shorthand syntax (:param) so middleware and route declarations use identical parameter strings./prefix/* middleware with /prefix* route handlers; standardize on slash-delimited wildcards (/prefix/*) alongside explicit root handlers (/prefix).Block Encoded Line Terminators at the Edge: Configure upstream reverse proxies, CDNs, or Web Application Firewalls (such as Cloudflare, Nginx, Envoy, or AWS WAF) to reject HTTP requests containing percent-encoded line terminators (%0A, %0D, %E2%80%A8, %E2%80%A9) in the request URI.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
hono honojs | >= 4.1.4, < 4.13.16 | 4.13.16 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-126358 (GHSA-h7mg-cq63-vhpq) |
| Vulnerability Class | CWE-863: Incorrect Authorization |
| Affected Component | Hono RegExpRouter (npm: hono >= 4.1.4, < 4.13.16) |
| CVSS v3.1 Score | 5.3 Medium (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N) |
| EPSS Score | 0.35% (28.76th Percentile) |
| Attack Vector | Unauthenticated Network HTTP Request |
| Exploit Status | Proof-of-Concept (Unit test reproduction available) |
| Patched Version | hono v4.13.16 |
The application performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check.
CVE-2026-126359 (GHSA-3gg2-335m-rxc6) is an improper input neutralization vulnerability (CWE-79) in the Hono web framework (`honojs/hono`). Versions from 3.12.0 prior to 4.13.16 evaluate and escape interpolated strings in the `hono/css` helper individually before concatenating template chunks and stripping CSS comments. When a `<` character and a `/style>` sequence are split across template boundaries, adjacent interpolations, or separated by a CSS comment, the sanitizer fails to escape the closing tag sequence. During Server-Side Rendering (SSR), the `<Style />` component outputs an unescaped `</style>` delimiter, permitting an attacker to break out of the CSS raw text context and execute arbitrary HTML and JavaScript in the victim's browser.
CVE-2026-126357 (GHSA-4m76-xfj5-7975) is an authentication bypass vulnerability in the Hono web application framework prior to version 4.13.16. The flaw resides in the prefix-stripping and URL reconstruction logic of `app.mount()` and the `mount()` middleware. Two distinct path transformation errors—static route-pattern length slicing on dynamic prefixes and URL normalization of decoded pathnames—create a routing discrepancy between the outer Hono router and the mounted sub-application. An unauthenticated attacker can exploit these discrepancies to bypass outer authentication or authorization middleware and access protected inner routes directly.
A parser differential vulnerability exists in the Hono web application framework versions 4.5.1 prior to 4.13.16. The built-in form validation middleware (`validator('form')`) utilizes an overly restrictive regular expression to evaluate the HTTP `Content-Type` header for `multipart/form-data` payloads. When a client supplies additional valid MIME parameters (such as `charset=utf-8`) or multiple whitespace characters following a semicolon delimiter, the regular expression fails to match. Consequently, the validator passes an empty object to the validation callback instead of the parsed form fields. If the validation logic permits empty objects or only checks for disallowed values on optional properties, the request passes validation, allowing downstream route handlers calling `c.req.parseBody()` or `c.req.formData()` to process unvalidated user input.
CVE-2026-126355 (GHSA-46wx-v44x-9hh2) is a Cross-Site Request Forgery (CSRF) validation bypass in Hono, a web application framework for JavaScript runtimes. Prior to version 4.13.16, Hono's built-in csrf() middleware only evaluated origin headers for requests matching standard HTML form MIME types (application/x-www-form-urlencoded, multipart/form-data, and text/plain). By leveraging the HTML anchor ping attribute in Chromium-based browsers, an attacker can initiate an unprompted, credentialed cross-origin HTTP POST request with Content-Type: text/ping without triggering a CORS preflight request. Because text/ping was omitted from the middleware regular expression, requests bypassed Origin and Sec-Fetch-Site verification, allowing unauthorized state-changing operations.
CVE-2026-128067 is a memory corruption vulnerability in Pillow (versions 2.3.0 through 12.3.0) occurring when saving compressed TIFF images via the underlying libtiff encoder. By supplying crafted image geometry tags through exif=, tiffinfo=, or a source TIFF IFD, an attacker can override the scanline dimensions configured in libtiff while Pillow allocates the internal row buffer based strictly on the physical image dimensions. This mismatch induces an out-of-bounds heap read during scanline encoding, and escalates to an out-of-bounds heap write when TIFF Predictor 2 (horizontal differencing) is enabled.
CVE-2026-128066 (GHSA-wqgw-9xfh-qf2g) is a resource allocation vulnerability (CWE-770) in the Python Pillow imaging library affecting versions 1.0 through 12.3.0. The flaw resides in the ImageFileDirectory_v2.load() method within src/PIL/TiffImagePlugin.py, which processes Tag Image File Format (TIFF) structures and Exchangeable Image File Format (EXIF) metadata across multiple container formats. By supplying an image with thousands of IFD tag entries that point to overlapping or identical file offsets, an attacker can trigger quadratic memory amplification (O(N * filesize)). Because the vulnerability occurs during metadata parsing rather than pixel decoding, it bypasses Pillow's built-in decompression bomb protections (Image.MAX_IMAGE_PIXELS).