Nov 13, 2026·9 min read·1 visit
Hono versions prior to 4.13.16 contain two path transformation flaws in `app.mount()` and `mount()` middleware. Slicing request paths by static pattern length instead of actual matched parameter length, combined with re-parsing decoded URL pathnames containing encoded backslashes or traversal sequences, allows unauthenticated attackers to bypass outer route middleware and reach protected sub-application endpoints.
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.
Hono is a lightweight web application framework designed to operate across JavaScript runtimes conforming to Web Standards, including Cloudflare Workers, Deno, Bun, and Node.js. A core architectural feature of Hono is the ability to compose applications by mounting independent Web Standard request handlers, third-party routers, or Hono sub-applications under a designated path prefix using app.mount() or the mount() middleware.
When the primary Hono application delegates an incoming HTTP request to a mounted handler, it must strip the mount prefix from the request URL so the sub-application can route the request relative to its own root. Prior to version 4.13.16, the prefix removal and URL reconstruction implementation contained two distinct flaws that caused the outer router and the mounted sub-application to evaluate different normalized paths for the same HTTP request.
Classified under CWE-288 (Authentication Bypass Using an Alternate Path or Channel), this discrepancy arises when authentication or authorization middleware is registered on the outer Hono application to protect specific subpaths inside a mounted application (for example, protecting /api/admin/* on an application mounted at /api). Because the outer router evaluates the raw or single-decoded path before prefix transformation, while the inner mounted application evaluates the path after prefix slicing and WHATWG URL re-normalization, an attacker can craft requests that evade the outer middleware filter but resolve to the protected inner path upon reaching the mounted handler.
The vulnerability stems from two separate logical defects in how Hono calculates the mount prefix length and reconstructs the Request object passed to the mounted handler.
In Hono versions prior to 4.13.16, both src/hono-base.ts and src/middleware/mount/index.ts determined how many characters to strip from the incoming request path by measuring the character length of the route pattern string (mergedPath.length) rather than the character length of the actual runtime segment matched by the router. When a mount path is static (such as /api), the pattern length and the matched prefix length are identical (4). However, when a mount prefix includes dynamic route parameters (such as /:tenant or /api/:version), the static string length of the pattern diverges from the length of the parameter supplied in the HTTP request.
For example, the pattern /:tenant has a static length of 8 characters. If a client sends a request where the :tenant parameter is only 1 character long (e.g., /a/...), the mount handler still strips 8 characters from the front of the pathname. This removes /a plus the subsequent 6 characters of the inner path. An attacker can insert arbitrary padding segments at the beginning of the inner path so that the outer router fails to match security middleware bound to /:tenant/admin/*, while the 8-character slice removes the padding and delivers /admin/* directly to the mounted application.
Starting in version 4.12.21, Hono reconstructed the forwarded URL by taking the decoded request pathname (this.getPath(request) or c.req.path), slicing off the mount prefix, and assigning the resulting decoded string directly to the .pathname property of a new URL(request.url) instance.
According to the WHATWG URL Standard implemented across JavaScript runtimes, assigning a string to url.pathname invokes the URL path setter state machine. This state machine treats both forward slashes (/) and backslashes (\) as path segment delimiters, converts all backslashes into forward slashes, and resolves relative dot segments (. and ..) by collapsing the preceding path segment.
When an incoming request contains percent-encoded backslashes (%5C) or double-encoded dot segments (%252e%252e), Hono's outer router decodes %5C into a literal \ character during initial path extraction. Because Hono's router only splits segments on forward slashes (/), it treats foo\.. or ..%5Cadmin as a single literal segment name and does not trigger middleware attached to /api/admin/*. However, when the mount handler assigns this decoded string containing a literal \ back into url.pathname, the runtime's WHATWG URL parser normalizes \ to / and collapses the .. traversal segment. The mounted sub-application subsequently receives a normalized Request targeting /admin/*.
Prior to commit 34f90432a1794b28e52802670efcd01963f716d6, the default replaceRequest implementation in src/hono-base.ts (and its counterpart in src/middleware/mount/index.ts) used static pattern length calculation and mutated url.pathname using the decoded path:
// Vulnerable implementation prior to 4.13.16
replaceRequest ||= (() => {
const mergedPath = mergePath(this._basePath, path)
// BUG 1: Uses static length of the route pattern (e.g., "/:tenant".length === 8)
const pathPrefixLength = mergedPath === '/' ? 0 : mergedPath.length
return (request) => {
const url = new URL(request.url)
// BUG 2: this.getPath(request) returns a decoded pathname.
// Assigning a decoded pathname containing '\' or '..' back to url.pathname
// triggers WHATWG URL normalization and directory traversal resolution.
url.pathname = this.getPath(request).slice(pathPrefixLength) || '/'
return new Request(url, request)
}
})()The patch in version 4.13.16 resolves both issues by introducing two dedicated utility modules: src/utils/mount.ts and stripPathPrefix in src/utils/url.ts.
First, getPathPrefixLength in src/utils/mount.ts inspects the router match results (c.req[GET_MATCH_RESULT]) at runtime to compute the exact length of the matched prefix string, accounting for dynamic parameter values and rejecting ambiguous wildcard or duplicate parameter configurations:
// Patched dynamic prefix length calculation in src/utils/mount.ts
// Extracts actual matched parameter values from the router match result
// rather than relying on the static template string length.Second, stripPathPrefix in src/utils/url.ts avoids assigning decoded paths back to url.pathname. Instead, it advances segment-by-segment through the raw URL pathname (url.pathname) in parallel with the decoded pathname up to the matched prefixLength, and slices the raw path directly:
// Patched prefix stripping in src/utils/url.ts
export const stripPathPrefix = (
rawPath: string,
decodedPath: string,
prefixLength: number
): string => {
if (rawPath.at(-1) === '/' && decodedPath.at(-1) !== '/') {
rawPath = rawPath.slice(0, -1)
}
let index = 0
for (
let i = decodedPath.indexOf('/');
i !== -1 && i < prefixLength;
i = decodedPath.indexOf('/', i + 1)
) {
index = rawPath.indexOf('/', index + 1)
if (index === -1) {
return '/'
}
}
// Returns a slice of rawPath, preserving percent-encoding such as %5C
// so new URL() does not reinterpret decoded backslashes as path separators.
return rawPath.slice(index) || '/'
}The fix addresses both root causes comprehensively. By mapping slash boundaries between decodedPath and rawPath and slicing rawPath, any percent-encoded characters (%5C, %2e) remain percent-encoded when assigned to url.pathname, preventing the WHATWG URL setter from converting %5C into / or collapsing .. segments that were not already present in the raw URL. Furthermore, validating mount route patterns at registration time prevents edge-case parameter misalignments.
Exploitation requires no authentication or special privileges, provided the target Hono application protects routes inside a mounted sub-application using outer Hono middleware. Depending on how the application is configured, an attacker can leverage either the dynamic prefix length discrepancy or the backslash traversal normalization.
Consider a multi-tenant Hono application that applies authentication middleware to /:tenant/admin/* on the main router and mounts an internal application at /:tenant:
import { Hono } from 'hono'
const internalApp = new Hono()
internalApp.get('/admin/secret', (c) => c.text('Confidential Admin Data'))
const app = new Hono()
app.all('/:tenant/admin/*', authMiddleware)
app.mount('/:tenant', internalApp.fetch)Here, the mount pattern /:tenant has a static length of 8 characters. An unauthenticated attacker sends the following request:
GET /a/publi/admin/secret HTTP/1.1
Host: target.example.com/a/publi/admin/secret against the middleware pattern /:tenant/admin/*. Here, :tenant is `The primary impact of CVE-2026-126357 is the bypass of authentication, authorization, rate-limiting, or access-control middleware applied at the outer Hono router level when protecting subpaths served by a mounted application.
Applications are only vulnerable if they meet two structural conditions:
app.mount() or the mount() middleware to delegate routing to a sub-application or secondary request handler.app.use('/api/*', auth)) or inside the mounted sub-application itself.Note that Attack Vector 1 (dynamic prefix slicing) affects all Hono versions prior to 4.13.16 when dynamic parameters are used in the mount path. Attack Vector 2 (backslash and traversal normalization via decoded path assignment) specifically affects Hono versions from 4.12.21 through 4.13.15 across any static or dynamic mount prefix.
The vulnerability is assigned a CVSS v3.1 base score of 5.3 (Medium) (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N). While the official vector records a Low Confidentiality impact (C:L) and no Integrity impact (I:N) under baseline assumptions, the concrete impact in production environments depends entirely on the functionality exposed by the bypassed inner routes. If the protected inner endpoints perform administrative state changes or expose sensitive tenant data, the practical confidentiality and integrity impact for that deployment increases accordingly.
The vulnerability is resolved in Hono version 4.13.16. Development and operations teams should upgrade the hono package to 4.13.16 or later immediately using their runtime package manager:
# npm
npm install hono@^4.13.16
# pnpm
pnpm update hono
# bun
bun update hono
# yarn
yarn upgrade honoIn environments where an immediate upgrade to 4.13.16 cannot be deployed, the following architectural mitigations eliminate the attack surface:
app.use('/api/*', authMiddleware)) rather than a nested subpath (app.use('/api/admin/*', authMiddleware)).app.mount('/:tenant', subApp)) with static mount prefixes until the package is updated.%5C, %5c), literal backslashes (\), or encoded directory traversal sequences (%2e%2e, %252e%252e) in the URI path.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.13.16 | 4.13.16 |
| Attribute | Detail |
|---|---|
| Vulnerability ID | CVE-2026-126357 / GHSA-4m76-xfj5-7975 |
| Weakness Class | CWE-288 (Authentication Bypass Using an Alternate Path or Channel) |
| 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) |
| Attack Vector | Network (Unauthenticated HTTP Request with crafted URL path) |
| Affected Versions | hono < 4.13.16 |
| Patched Version | hono 4.13.16 |
| Exploit Status | No known public exploits |
| CISA KEV Status | Not Listed |
A product requires authentication, but the product has an alternate path or channel that does not require authentication.
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).
CVE-2026-126081 is a mass-assignment vulnerability (CWE-915) in the Action Pack component of Ruby on Rails. Prior to versions 8.1.4.1 and 8.0.5.2, the ActionController::Parameters#each_key method was delegated directly to the internal ActiveSupport::HashWithIndifferentAccess instance holding raw request parameters. When invoked with a block, #each_key returned the underlying unpermitted hash rather than the ActionController::Parameters wrapper instance. Applications that pass the return value of #each_key directly into Active Model mass-assignment methods bypass Strong Parameters enforcement, allowing attackers to modify restricted model attributes.
A vulnerability classified under CWE-345 (Insufficient Verification of Data Authenticity) exists in the Active Storage component of Ruby on Rails prior to versions 8.1.4.1 and 8.0.5.2. When Google Cloud Storage (GCS) is configured as the storage backend with the cache_control option enabled, Active Storage switches from GCS V2 signing to V4 signing for direct upload URLs. Because the V4 signing implementation only binds the Cache-Control and Host headers, the Content-Type and Content-MD5 headers remain unsigned. An attacker can modify the Content-Type header during a direct upload request to store HTML or SVG files with active JavaScript on the GCS bucket origin, enabling Service Worker registration and interception of subsequent signed URL responses within the bucket path.