Nov 13, 2026·10 min read·2 visits
Hono versions 4.5.1 to 4.13.15 fail to validate `multipart/form-data` requests when the `Content-Type` header includes extra parameters or multiple spaces after a semicolon. This parser differential passes an empty object to the validator while exposing the full unvalidated payload to downstream handlers via `c.req.parseBody()` or `c.req.formData()`.
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.
Hono is a lightweight web application framework designed to operate across JavaScript runtimes supporting Web Standards and the Fetch API, including Cloudflare Workers, Deno, Bun, and Node.js. To enforce schema and input integrity on incoming HTTP requests, Hono provides a built-in validation middleware accessed via validator('form', callback). This middleware intercepts incoming form submissions, parses the request body, and passes the extracted key-value pairs to a developer-defined validation function or schema validator before the main route handler executes.
In Hono versions 4.5.1 through 4.13.15, the validator('form') middleware exhibits an input validation flaw classified under CWE-20 (Improper Input Validation) and CWE-1471 (Insufficiently Controlled Parser Differential). The middleware determines whether to parse an incoming request body as multipart data by matching the HTTP Content-Type header against an internal regular expression (multipartRegex). Because this regular expression was written to match only a strict subset of valid multipart/form-data headers, any deviation in parameter ordering, additional MIME parameters, or extra whitespace causes the pattern match to fail.
When multipartRegex fails to match the Content-Type header, the validator middleware skips body parsing entirely and invokes the validation callback with an empty JavaScript object ({}). However, when the request proceeds to the downstream route handler, methods such as c.req.parseBody() or c.req.formData() rely on the underlying runtime's native Fetch API parser. Because standard runtime parsers adhere to RFC 2046 and RFC 7231, they successfully parse the multipart payload despite the extra parameters or whitespace, exposing completely unvalidated fields to the application's business logic.
> [!NOTE]
> This vulnerability specifically impacts multipart/form-data requests. Form submissions encoded as application/x-www-form-urlencoded are evaluated by a separate regular expression that already accounted for arbitrary parameters and whitespace, rendering them unaffected.
The root cause of CVE-2026-126356 is a discrepancy between how Hono's validation layer identifies multipart requests and how the underlying JavaScript runtime parses them. Within src/validator/validator.ts, Hono inspects the Content-Type header of incoming requests when validator('form') is registered on a route. Prior to version 4.13.16, the regular expression responsible for identifying multipart/form-data headers was defined with strict positional and character constraints.
Specifically, the vulnerable regular expression ^multipart\/form-data(;\s?boundary=[a-zA-Z0-9'"()+_,\-./:=?]+)?$ enforced two non-standard limitations on the HTTP header format:
boundary directive to be the sole parameter following the multipart/form-data media type. Under RFC 2046 Section 5.1 and RFC 7231 Section 3.1.1.1, media types may include additional parameters such as charset (e.g., Content-Type: multipart/form-data; charset=utf-8; boundary=Example), and parameters may appear in any order.\s? immediately after the semicolon delimiter, permitting at most a single whitespace character. RFC 7230 permits optional whitespace (OWS), meaning zero or more spaces or horizontal tabs (; boundary=Example) are valid according to HTTP specifications.When a request containing either multiple spaces after the semicolon or an auxiliary parameter like charset=utf-8 reaches the middleware, both multipartRegex.test(contentType) and urlencodedRegex.test(contentType) evaluate to false. Instead of rejecting the request with a 400 Bad Request or 415 Unsupported Media Type status, the validator defaults to an unpopulated value object ({}) and passes it directly to the user's validation callback.
The parser differential manifests when the validation callback completes and calls next(). Downstream handlers frequently access form inputs by calling await c.req.parseBody() or await c.req.formData(), rather than reading strictly from c.req.valid('form'), or the validation callback itself only asserts constraints on properties if they are present (such as blocklisting specific values or validating optional fields). Because the underlying runtime (such as Node.js undici, Bun, Deno, or Cloudflare Workers) uses an RFC-compliant MIME parser, it ignores the extra whitespace or auxiliary parameters, extracts the multipart boundary, and populates the form data structure with the attacker's payload.
The vulnerability was introduced in commit 0535579de5af90319165304be409ea9b7d5d47f3 (released in Hono 4.5.1) and resolved in commit d88fab372415277a9e540e895650943dd08eef4a (released in Hono 4.13.16). Inspection of src/validator/validator.ts reveals that jsonRegex and urlencodedRegex were already constructed to accept arbitrary sequences of semicolon-delimited parameters and variable whitespace, whereas multipartRegex had been hardcoded to look exclusively for a single boundary parameter.
// Vulnerable definitions in src/validator/validator.ts (prior to 4.13.16)
const jsonRegex = /^application\/([a-z-\.]+\+)?json(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/i
// FLAW: Only allows 0 or 1 space (\s?) and only matches a single 'boundary=' parameter
const multipartRegex = /^multipart\/form-data(;\s?boundary=[a-zA-Z0-9'"()+_,\-./:=?]+)?$/i
const urlencodedRegex = /^application\/x-www-form-urlencoded(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/iIn commit d88fab372415277a9e540e895650943dd08eef4a, the maintainers aligned multipartRegex with the structure used by urlencodedRegex and jsonRegex:
--- a/src/validator/validator.ts
+++ b/src/validator/validator.ts
@@ -22,7 +22,7 @@ export type ValidationFunction<
) => OutputType | TypedResponse | Promise<OutputType> | Promise<TypedResponse>
const jsonRegex = /^application\/([a-z-\.]+\+)?json(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/i
-const multipartRegex = /^multipart\/form-data(;\s?boundary=[a-zA-Z0-9'"()+_,\-./:=?]+)?$/i
+const multipartRegex = /^multipart\/form-data(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/i
const urlencodedRegex = /^application\/x-www-form-urlencoded(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/i
export type ExtractValidationResponse<VF> = VF extends (value: any, c: any) => infer RThe updated pattern (;\s*[a-zA-Z0-9\-]+\=([^;]+))* makes two critical adjustments:
\s? with \s*, allowing zero or more whitespace characters after each semicolon.boundary=... requirement with a repeating group ([a-zA-Z0-9\-]+\=([^;]+))*, allowing any number of valid parameter key-value pairs in any order.To verify the fix, the commit added regression tests in src/validator/validator.test.ts testing three distinct header permutations that previously bypassed validation:
// Added in src/validator/validator.test.ts
it('Should validate multipart/form-data with extra parameters', async () => {
const formData = new FormData()
formData.append('foo', 'bar')
const source = new Request('http://localhost/post', { method: 'POST', body: formData })
const boundary = source.headers.get('Content-Type')!.replace(/^multipart\/form-data;\s*/, '')
for (const contentType of [
`multipart/form-data; ${boundary}; charset=utf-8`, // Extra parameter after boundary
`multipart/form-data; charset=utf-8; ${boundary}`, // Extra parameter before boundary
`multipart/form-data; ${boundary}`, // Multiple spaces after semicolon
]) {
const res = await app.request('/post', {
method: 'POST',
body: await source.clone().arrayBuffer(),
headers: {
'content-type': contentType,
},
})
expect(res.status).toBe(200)
expect(await res.json()).toEqual({
foo: 'bar',
})
}
})Regarding fix completeness, aligning multipartRegex with urlencodedRegex significantly narrows the parser differential for standard parameter lists. However, one subtle edge case remains in the regex (;\s*[a-zA-Z0-9\-]+\=([^;]+))*: if a client sends a trailing semicolon without a parameter (e.g., multipart/form-data; boundary=xyz;) or includes quoted string parameters containing an embedded semicolon (e.g., param="a;b"), the regex may still fail to match while lenient runtime parsers might still accept the body. Therefore, applications should also ensure they consume validated data strictly via c.req.valid('form') rather than re-invoking c.req.parseBody() in the route handler.
Exploiting CVE-2026-126356 requires no authentication or special privileges, provided the target endpoint is accessible to the client and utilizes validator('form'). Two application-level conditions are necessary for a successful bypass:
validator('form') must permit an empty object ({}). This commonly occurs when endpoints validate optional profile attributes, implement blocklists against restricted field values (e.g., checking if (value.role === 'admin')), or use schema definitions where all fields have default or optional modifiers.c.req.parseBody() or c.req.formData() instead of exclusively relying on the empty object returned by c.req.valid('form').Consider the following vulnerable Hono endpoint designed to prevent users from escalating their privilege role during a profile update:
import { Hono } from 'hono'
import { validator } from 'hono/validator'
const app = new Hono()
app.post(
'/profile',
validator('form', (value, c) => {
// Security check intended to block administrative role assignment
if (value.role && value.role === 'admin') {
return c.text('Unauthorized role modification', 403)
}
return value
}),
async (c) => {
// Handler reads parsed body from the underlying runtime
const body = await c.req.parseBody()
return c.json({ status: 'updated', user: body })
}
)In a standard request where Content-Type: multipart/form-data; boundary=----Boundary is sent with a role=admin field, multipartRegex matches, value is populated with { role: 'admin' }, and the validator returns a 403 Forbidden response.
To bypass this restriction, an attacker modifies the Content-Type header by inserting either a second space after the semicolon or a benign parameter such as charset=utf-8:
POST /profile HTTP/1.1
Host: target.example.com
Content-Type: multipart/form-data; charset=utf-8; boundary=----WebKitFormBoundaryDummyBoundary
Content-Length: 184
------WebKitFormBoundaryDummyBoundary
Content-Disposition: form-data; name="role"
admin
------WebKitFormBoundaryDummyBoundary
Content-Disposition: form-data; name="username"
attacker
------WebKitFormBoundaryDummyBoundary--When Hono processes this crafted request:
validator('form') tests multipart/form-data; charset=utf-8; boundary=----WebKitFormBoundaryDummyBoundary against multipartRegex, which returns false.{} as value to the callback.value.role evaluates to undefined, bypassing the if (value.role && value.role === 'admin') check.await c.req.parseBody() invokes the runtime's multipart parser.{ role: 'admin', username: 'attacker' }, completing the unauthorized state modification.CVE-2026-126356 is rated with a CVSS v3.1 base score of 5.3 (Medium) under the vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N. The attack vector is Network (AV:N) with Low attack complexity (AC:L), requiring neither privileges (PR:N) nor user interaction (UI:N). The primary impact is on Integrity (I:L), as attackers can supply unvalidated or prohibited form fields to application handlers without affecting confidentiality (C:N) or availability (A:N).
The concrete security impact depends on the business logic protected by validator('form'). In applications that use the form validator for mass-assignment protection, field sanitization, or input allow-listing while subsequently reading from c.req.parseBody() or c.req.formData(), an attacker can inject arbitrary form fields or file uploads. This can lead to unauthorized privilege escalation (e.g., overwriting internal user flags or roles), stored Cross-Site Scripting (XSS) if HTML sanitization occurs inside the validator callback, or business logic manipulation.
Conversely, applications are not impacted if they meet either of the following criteria:
{} object with a 400 Bad Request error).c.req.valid('form') and never calls c.req.parseBody() or c.req.formData() directly (in which case the handler only receives the empty {} object).All runtime adapters built around Hono's core request handling—including @hono/node-server, @hono/bun, @hono/deno, @hono/cloudflare-workers, @hono/vercel, @hono/aws-lambda, and @hono/netlify—inherit this vulnerability when running affected versions of the core hono package.
The primary remediation is to upgrade the hono dependency to version 4.13.16 or later, which includes commit d88fab372415277a9e540e895650943dd08eef4a updating multipartRegex to handle extra whitespace and additional MIME parameters.
# Using npm
npm install hono@^4.13.16
# Using pnpm
pnpm update hono@^4.13.16
# Using Bun
bun add hono@^4.13.16In addition to upgrading the framework, developers should adopt two architectural practices to eliminate parser differential risks across Hono endpoints:
c.req.valid('form') rather than re-parsing the raw request via c.req.parseBody() or c.req.formData(). This ensures the handler operates on the exact data structure inspected by the validator.app.post(
'/profile',
validator('form', (value, c) => {
if (!value || Object.keys(value).length === 0) {
return c.text('Empty or malformed form payload', 400)
}
if (value.role === 'admin') {
return c.text('Unauthorized role modification', 403)
}
return value
}),
async (c) => {
// SAFE: Accesses the exact object returned by the validator
const body = c.req.valid('form')
return c.json({ status: 'updated', user: body })
}
)Object.keys(value).length === 0) on endpoints expecting form fields. Furthermore, edge proxies or Web Application Firewalls (WAFs) can be configured to block or normalize multipart/form-data headers that contain multiple semicolons or non-boundary parameters.CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
hono honojs | >= 4.5.1, < 4.13.16 | 4.13.16 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-126356 (GHSA-4h7h-rv7c-7m2v) |
| Primary CWE | CWE-20 (Improper Input Validation) / CWE-1471 (Parser Differential) |
| CVSS v3.1 Score | 5.3 Medium (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N) |
| Attack Vector | Network (Unauthenticated HTTP multipart/form-data request) |
| Affected Versions | hono >= 4.5.1, < 4.13.16 |
| Patched Version | hono 4.13.16 |
| Exploit Status | Proof-of-Concept (Regression tests available) |
| CISA KEV Status | Not Listed |
The product does not validate or incorrectly validates input that can affect the control flow or data flow of a program, driven by an underlying parser differential (CWE-1471) between the validation middleware and the runtime body parser.
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.
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.