Hono ErrorBoundary: When the Safety Net is the Trap
Jan 29, 2026·5 min read·2 visits
Executive Summary (TL;DR)
Hono's `ErrorBoundary` component was manually marking its child content as 'raw' (safe) HTML. This allowed attackers to inject malicious scripts if user input was rendered inside the boundary. Fixed in v4.11.7 by using standard JSX Fragments.
In a twist of irony, the component designed to handle crashes in the Hono framework became a vector for crashes of a different kind. CVE-2026-24771 reveals how the `ErrorBoundary` component in Hono's JSX middleware bypassed the framework's own XSS protections, turning harmless error states into dangerous injection vectors.
The Hook: The Safety Net That Wasn't
We all love an ErrorBoundary. It's the digital equivalent of an airbag—there to catch you when your code inevitably hits a wall. In the React ecosystem and its spiritual successors like Hono, these components are supposed to be inert wrappers that only wake up when something goes boom.
But in hono/jsx, the airbag was packed with shrapnel. The ErrorBoundary component wasn't just catching errors; it was aggressively interfering with the rendering pipeline. Instead of letting the JSX engine do its job (which includes sanitizing untrusted input), the component took a shortcut that effectively disabled the framework's immune system.
This vulnerability is a classic reminder that "helper" code often bypasses the strict security controls applied to the core logic. While developers were busy sanitizing their inputs elsewhere, this component was quietly whispering to the renderer: "Don't worry about this data, I've checked it. It's totally safe." Spoiler alert: It wasn't.
The Flaw: Accidental Trust
The root of the issue lies in how Hono handled the rendering of children within the ErrorBoundary. In a typical JSX environment, if you try to render a string like <script>alert(1)</script>, the engine escapes the angle brackets to < and >. This is XSS Defense 101.
However, the ErrorBoundary implementation in versions prior to 4.11.7 decided to go rogue. Instead of passing the children down the pipeline as nodes, it manually converted them to strings, concatenated them, and then—here is the fatal flaw—wrapped the entire blob in a raw() function call.
In Hono, raw() is the magic word that tells the framework, "This string is already safe HTML. Do not escape it." By applying this to the entire output of the component, the developers inadvertently authorized any malicious payload contained within the children to execute in the browser. It’s the equivalent of checking IDs at the club entrance but letting anyone with a "VIP" sticker walk in with a flamethrower—and then slapping a "VIP" sticker on everyone who walks through the door.
The Code: The Smoking Gun
Let's look at the crime scene in src/jsx/components.ts. The code tries to manage asynchronous children (Promises) and synchronous children together. In doing so, it forces a conversion to string and then bypasses safety checks.
The Vulnerable Code (Pre-4.11.7):
// 1. Convert everything to a string
resArray = children.map((c) =>
c == null || typeof c === 'boolean' ? '' : c.toString()
)
// ... logic for promises ...
// 2. The Fatal Flaw: Joining strings and marking them as RAW
return raw(resArray.join(''))The function c.toString() returns the string representation. If c is the string <img src=x...>, toString() returns exactly that. Then raw() marks it as safe.
The Fix (4.11.7):
import { Fragment } from './base'
// The fix relies on standard JSX composition
// We cast to Child[] and let the engine decide how to render it
return Fragment({ children: resArray as Child[] })The fix is elegant in its simplicity. Instead of hacking the string concatenation, the maintainers switched to using a Fragment. This keeps the children as distinct nodes in the virtual DOM (or Hono's equivalent), ensuring that string nodes are subjected to standard escaping rules before they hit the wire.
The Exploit: Crashing into XSS
Exploiting this requires a scenario where an attacker can reflect input inside an ErrorBoundary. While ErrorBoundary is usually placed high up in the component tree (like in a Layout), developers often use it granularly around widgets.
The Setup:
Imagine a developer wraps a user profile card in an ErrorBoundary because the profile data fetches might fail.
app.get('/user', (c) => {
const name = c.req.query('name') // User controlled!
return c.html(
<ErrorBoundary fallback={<div>Failed to load</div>}>
<div class="profile">
Welcome, {name}
</div>
</ErrorBoundary>
)
})The Payload:
An attacker sends a link: https://example.com/user?name=<img src=x onerror=alert(document.cookie)>
The Execution Flow:
- The
namevariable enters the JSX tree. - Inside
<ErrorBoundary>,nameis part of thechildrenarray. - The vulnerable component calls
.toString()on the child, preserving the<img...>tag. - It joins the array and wraps it in
raw(). - The browser receives unescaped HTML and executes the JavaScript.
This bypasses the developer's expectation that {name} is safe because it's inside curly braces. It turns a standard reflected value into a stored-like XSS vector.
The Mitigation: Patch or Perish
The fix is straightforward: Update hono to version 4.11.7 or higher. The Hono team responded quickly, and the patch essentially removes the dangerous logic entirely in favor of standard composition.
If you are stuck on an older version (perhaps due to other dependencies), you are in a tight spot. You cannot monkey-patch the ErrorBoundary easily without forking the package. Your best interim defense is to manually sanitize every variable passed into an ErrorBoundary using Hono's escapeHtml utility:
import { escapeHtml } from 'hono/utils/html'
<ErrorBoundary>
{escapeHtml(userInput)} {/* Double escaping is better than XSS */}
</ErrorBoundary>However, since ErrorBoundary is often used deeply within libraries or layouts, finding every usage is tedious and error-prone. Just upgrade the package. It's lighter than your guilt will be after a breach.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:NAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
hono Hono | < 4.11.7 | 4.11.7 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| CVSS Score | 4.7 (Medium) |
| Vector | CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N |
| EPSS Score | 0.00036 |
| Attack Vector | Network / Reflected |
| Vendor | Hono |
MITRE ATT&CK Mapping
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.