Описание
h3: Missing Path Segment Boundary Check in mount() Causes Middleware Execution on Unrelated Prefix-Matching Routes
Summary
The mount() method in h3 uses a simple startsWith() check to determine whether incoming requests fall under a mounted sub-application's path prefix. Because this check does not verify a path segment boundary (i.e., that the next character after the base is / or end-of-string), middleware registered on a mount like /admin will also execute for unrelated routes such as /admin-public, /administrator, or /adminstuff. This allows an attacker to trigger context-setting middleware on paths it was never intended to cover, potentially polluting request context with unintended privilege flags.
Details
The root cause is in src/h3.ts:127 within the mount() method:
When a sub-app is mounted at /admin, the check originalPathname.startsWith("/admin") returns true for /admin, /admin/, /admin/dashboard, but also for /admin-public, /administrator, /adminFoo, etc. The mounted sub-app's entire middleware chain then executes for these unrelated paths.
A secondary instance of the same flaw exists in src/utils/internal/path.ts:40:
The withoutBase() utility will incorrectly strip the base from paths that merely share a string prefix, returning mangled paths (e.g., withoutBase("/admin-public/info", "/admin") returns /-public/info).
Exploitation flow:
- Developer mounts a sub-app at
/adminwith middleware that setsevent.context.isAdmin = true - Developer defines a separate route
/admin-public/infoon the parent app that readsevent.context.isAdmin - Attacker requests
GET /admin-public/info - The
/adminmount'sstartsWithcheck passes → admin middleware executes → setsisAdmin = true - The middleware's "restore pathname" callback fires, control returns to the parent app
- The
/admin-public/infohandler seesevent.context.isAdmin === true
PoC
Steps to reproduce:
Impact
- Context pollution across mount boundaries: Middleware registered on a mounted sub-app executes for any route sharing the string prefix, not just routes under the intended path segment tree. This can set privileged flags (
isAdmin,isAuthenticated, role assignments) on requests to completely unrelated routes. - Authorization bypass: If an application uses mount-scoped middleware to set permissive context flags and other routes check those flags, an attacker can access protected functionality by requesting a path that string-prefix-matches the mount base but routes to a different handler.
- Path mangling: The
withoutBase()utility produces incorrect paths (e.g.,/-public/infoinstead of/admin-public/info) when the input shares only a string prefix, potentially causing routing errors or further security issues in downstream path processing. - Scope: Any h3 v2 application using
mount()with a base path that is a string prefix of other routes is affected. The impact scales with how the application uses middleware-set context values.
Recommended Fix
Add a segment boundary check after the startsWith call in both locations. The character immediately following the base prefix must be /, ?, #, or the string must end exactly at the base:
Fix for src/h3.ts:127:
Fix for src/utils/internal/path.ts:40:
This ensures that /admin only matches /admin, /admin/, and /admin/... — never /admin-public, /administrator, or other coincidental string-prefix matches.
Пакеты
h3
>= 2.0.1-alpha.0, <= 2.0.1-rc.16
2.0.1-rc.17
Связанные уязвимости
H3 is a minimal H(TTP) framework. In versions 2.0.0-0 through 2.0.1-rc.16, the `mount()` method in h3 uses a simple `startsWith()` check to determine whether incoming requests fall under a mounted sub-application's path prefix. Because this check does not verify a path segment boundary (i.e., that the next character after the base is `/` or end-of-string), middleware registered on a mount like `/admin` will also execute for unrelated routes such as `/admin-public`, `/administrator`, or `/adminstuff`. This allows an attacker to trigger context-setting middleware on paths it was never intended to cover, potentially polluting request context with unintended privilege flags. Version 2.0.2-rc.17 contains a patch.
H3 is a minimal H(TTP) framework. In versions 2.0.0-0 through 2.0.1-rc.16, the `mount()` method in h3 uses a simple `startsWith()` check to determine whether incoming requests fall under a mounted sub-application's path prefix. Because this check does not verify a path segment boundary (i.e., that the next character after the base is `/` or end-of-string), middleware registered on a mount like `/admin` will also execute for unrelated routes such as `/admin-public`, `/administrator`, or `/adminstuff`. This allows an attacker to trigger context-setting middleware on paths it was never intended to cover, potentially polluting request context with unintended privilege flags. Version 2.0.2-rc.17 contains a patch.