{"id":"CVE-2026-33807","revision":null,"records":[{"revision":"b51ef03939ab49c4a3b4525ecbc9bc36","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-33807","aliases":[],"title":"@fastify/express vulnerable to middleware path doubling causing authentication bypass in child plugin scopes","description":"@fastify/express v4.0.4 and earlier contains a path handling bug in the onRegister function that causes middleware paths to be doubled when inherited by child plugins. When a child plugin is registered with a prefix that matches a middleware path, the middleware path is prefixed a second time, causing it to never match incoming requests. This results in complete bypass of Express middleware security controls, including authentication, authorization, and rate limiting, for all routes defined within affected child plugin scopes. No special configuration or request crafting is required.\n\nUpgrade to @fastify/express v4.0.5 or later.","state":0,"publishedAt":"2026-04-15T09:52:26.838+00:00","modifiedAt":"2026-04-15T13:09:45.259+00:00","affected":[{"ecosystem":"Vendor product","name":"@fastify/express","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"fastify\",\n                    \"product\": \"@fastify/express\",\n                    \"versions\": [\n                        {\n                            \"status\": \"affected\",\n                            \"version\": \"0\",\n                            \"lessThan\": \"4.0.5\",\n                            \"versionType\": \"semver\"\n                        },\n                        {\n                            \"status\": \"unaffected\",\n                            \"version\": \"4.0.5\",\n                            \"versionType\": \"semver\"\n                        }\n                    ],\n                    \"defaultStatus\": \"unaffected\",\n                    \"packageURL\": \"pkg:npm/@fastify/express\"\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N","score":9.1,"severity":"CRITICAL"}],"references":["https://github.com/fastify/fastify-express/security/advisories/GHSA-hrwm-hgmj-7p9c","https://cna.openjsf.org/security-advisories.html"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-33807","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-33807","identifiers":["CVE-2026-33807"]},"retained":false,"historicalAffected":[]},{"revision":"38269f98bc1d41c78f9e798362379cb4","observedAt":"2026-11-01T05:16:06.111+00:00","statement":{"source":"euvd","id":"EUVD-2026-22880","aliases":["CVE-2026-33807"],"title":"@fastify/express v4.0.4 and earlier contains a path handling bug in the onRegister function that causes middleware paths to be doubled when inherited by child plugins. When a child plugin is registered with a prefix that matches a middlewar…","description":"@fastify/express v4.0.4 and earlier contains a path handling bug in the onRegister function that causes middleware paths to be doubled when inherited by child plugins. When a child plugin is registered with a prefix that matches a middleware path, the middleware path is prefixed a second time, causing it to never match incoming requests. This results in complete bypass of Express middleware security controls, including authentication, authorization, and rate limiting, for all routes defined within affected child plugin scopes. No special configuration or request crafting is required.\n\nUpgrade to @fastify/express v4.0.5 or later.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N","score":9.1,"severity":null}],"references":["https://github.com/fastify/fastify-express/security/advisories/GHSA-hrwm-hgmj-7p9c","https://cna.openjsf.org/security-advisories.html"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-22880","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Apr 15, 2026, 9:52:26 AM","dateUpdated":"Apr 15, 2026, 1:09:45 PM"},"relatedAdvisories":[],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-22880","identifiers":["EUVD-2026-22880","CVE-2026-33807"]},"retained":false,"historicalAffected":[]},{"revision":"a6ff3c6544954ce4bfdcaa9061689b00","observedAt":"2026-10-27T09:02:55.767+00:00","statement":{"source":"nvd","id":"CVE-2026-33807","aliases":[],"title":"@fastify/express v4.0.4 and earlier contains a path handling bug in the onRegister function that causes middleware paths to be doubled when inherited by child plugins. When a child plugin is registered with a prefix that matches a middlewar…","description":"@fastify/express v4.0.4 and earlier contains a path handling bug in the onRegister function that causes middleware paths to be doubled when inherited by child plugins. When a child plugin is registered with a prefix that matches a middleware path, the middleware path is prefixed a second time, causing it to never match incoming requests. This results in complete bypass of Express middleware security controls, including authentication, authorization, and rate limiting, for all routes defined within affected child plugin scopes. No special configuration or request crafting is required.\n\nUpgrade to @fastify/express v4.0.5 or later.","state":0,"publishedAt":"2026-04-15T10:16:48.31+00:00","modifiedAt":"2026-10-26T21:10:00.17+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · ce714d77-add3-4f53-aff5-83d477b104bb · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N","score":9.1,"severity":"CRITICAL"}],"references":["https://cna.openjsf.org/security-advisories.html","https://github.com/fastify/fastify-express/security/advisories/GHSA-hrwm-hgmj-7p9c"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-33807","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Analyzed","weaknesses":["CWE-436"],"productCriteria":["cpe:2.3:a:fastify:fastify\\/express:*:*:*:*:*:node.js:*:*"],"sourceDates":{"published":"2026-04-15T10:16:48.310","lastModified":"2026-10-26T21:10:00.170"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-33807","identifiers":["CVE-2026-33807"]},"retained":false,"historicalAffected":[]},{"revision":"447ba4e4f5e642d5901a1c354b228126","observedAt":"2026-10-24T13:17:17.793+00:00","statement":{"source":"osv","id":"GHSA-hrwm-hgmj-7p9c","aliases":["CVE-2026-33807"],"title":"@fastify/express's middleware path doubling causes authentication bypass in child plugin scopes","description":"### Summary\n\n`@fastify/express` v4.0.4 contains a path handling bug in the `onRegister` function that causes middleware paths to be doubled when inherited by child plugins. This results in complete bypass of Express middleware security controls for all routes defined within child plugin scopes that share a prefix with parent-scoped middleware. No special configuration is required — this affects the default Fastify configuration.\n\n### Details\n\nThe vulnerability exists in the `onRegister` function at `index.js` lines 92-101. When a child plugin is registered with a prefix, the `onRegister` hook copies middleware from the parent scope and re-registers it using `instance.use(...middleware)`. However, the middleware paths stored in `kMiddlewares` are already prefixed from their original registration.\n\nThe call flow demonstrates the problem:\n1. Parent scope registers middleware: `app.use('/admin', authFn)` — `use()` calculates path as `'' + '/admin' = '/admin'` — stores `['/admin', authFn]` in `kMiddlewares`\n2. Child plugin registers with `{ prefix: '/admin' }` — triggers `onRegister(instance)`\n3. `onRegister` copies parent middleware and calls `instance.use('/admin', authFn)` on child\n4. Child's `use()` function calculates path as `'/admin' + '/admin' = '/admin/admin'` — registers middleware with doubled path\n5. Routes in child scope use the child's Express instance, where middleware is registered under the incorrect path `/admin/admin`\n6. Requests to `/admin/secret` don't match `/admin/admin` — middleware is silently skipped\n\nThe root cause is in the `use()` function at lines 25-26, which always prepends `this.prefix` to string paths, combined with `onRegister` re-calling `use()` with already-prefixed paths.\n\n### PoC\n\n```javascript\nconst fastify = require('fastify');\nconst http = require('http');\n\nfunction get(port, url) {\n  return new Promise((resolve, reject) => {\n    http.get('http://localhost:' + port + url, (res) => {\n      let data = '';\n      res.on('data', (chunk) => data += chunk);\n      res.on('end', () => resolve({ status: res.statusCode, body: data }));\n    }).on('error', reject);\n  });\n}\n\nasync function test() {\n  const app = fastify({ logger: false });\n  await app.register(require('@fastify/express'));\n  \n  // Middleware enforcing auth on /admin routes\n  app.use('/admin', function(req, res, next) {\n    if (!req.headers.authorization) {\n      res.statusCode = 403;\n      res.setHeader('content-type', 'application/json');\n      res.end(JSON.stringify({ error: 'Forbidden' }));\n      return;\n    }\n    next();\n  });\n  \n  // Root scope route — middleware works correctly\n  app.get('/admin/root-data', async () => ({ data: 'root-secret' }));\n  \n  // Child scope route — middleware BYPASSED\n  await app.register(async function(child) {\n    child.get('/secret', async () => ({ data: 'child-secret' }));\n  }, { prefix: '/admin' });\n  \n  await app.listen({ port: 19876, host: '0.0.0.0' });\n  \n  // Root scope: correctly blocked\n  let r = await get(19876, '/admin/root-data');\n  console.log('/admin/root-data (no auth):', r.status, r.body);\n  // Output: 403 {\"error\":\"Forbidden\"}\n  \n  // Child scope: BYPASSED — secret data returned without auth\n  r = await get(19876, '/admin/secret');\n  console.log('/admin/secret (no auth):', r.status, r.body);\n  // Output: 200 {\"data\":\"child-secret\"}\n  \n  await app.close();\n}\ntest();\n```\n\nActual output:\n```\n/admin/root-data (no auth): 403 {\"error\":\"Forbidden\"}\n/admin/secret (no auth): 200 {\"data\":\"child-secret\"}\n```\n\n### Impact\n\nComplete bypass of Express middleware security controls for all routes defined in child plugin scopes. Authentication, authorization, rate limiting, CSRF protection, audit logging, and any other middleware-based security mechanisms are silently skipped for affected routes.\n\n- No special request crafting is required — normal requests bypass the middleware\n- It affects the idiomatic Fastify plugin pattern commonly used in production\n- The bypass is silent with no errors or warnings\n- Developers' basic testing of root-scoped routes will pass, masking the vulnerability\n- Any child plugin scope that shares a prefix with middleware is affected\n\nApplications using `@fastify/express` with path-scoped middleware and child plugins with matching prefixes are vulnerable in default configurations.\n\n### Affected Versions\n\n- `@fastify/express` v4.0.4 (latest at time of discovery)\n- Fastify 5.x in default configuration\n- No special router options required (`ignoreDuplicateSlashes` not needed)\n- Affects any child plugin registration where the prefix overlaps with middleware path scoping\n- Does NOT affect middleware registered without path scoping (global middleware)\n- Does NOT affect middleware registered on root path (`/`) due to special case handling\n\n### Variant Testing\n\n| Scenario | Middleware Path | Child Prefix | Result |\n|---|---|---|---|\n| Root route `/admin/root-data` | `/admin` | N/A | Middleware runs (403) |\n| Child route `/admin/secret` | `/admin` | `/admin` | **BYPASS** (200) |\n| Child route `/api/data` | `/api` | `/api` | **BYPASS** (200) |\n| Nested child `/admin/sub/data` | `/admin` | `/admin/sub` | **BYPASS** — path becomes `/admin/sub/admin` |\n| Middleware on `/` with any child | `/` | `/api` | No bypass — `path === '/' && prefix.length > 0` special case |\n\n### Suggested Fix\n\nThe `onRegister` function should store and re-use the original unprefixed middleware paths, or avoid re-calling the `use()` function entirely. Options include:\n1. Store the original path and function separately in `kMiddlewares` before prefixing\n2. Strip the parent prefix before re-registering in child scopes\n3. Store already-constructed Express middleware objects rather than re-processing paths","state":0,"publishedAt":"2026-04-16T01:03:25+00:00","modifiedAt":"2026-07-08T08:14:43.215389+00:00","affected":[{"ecosystem":"npm","name":"@fastify/express","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"4.0.5"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"CRITICAL"}],"references":["https://github.com/fastify/fastify-express/security/advisories/GHSA-hrwm-hgmj-7p9c","https://nvd.nist.gov/vuln/detail/CVE-2026-33807","https://cna.openjsf.org/security-advisories.html","https://github.com/fastify/fastify-express"],"sourceUrl":"https://github.com/advisories/GHSA-hrwm-hgmj-7p9c","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-hrwm-hgmj-7p9c","identifiers":["GHSA-hrwm-hgmj-7p9c","CVE-2026-33807"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-01T15:57:20.8239354+00:00","complete":true,"records":1766,"lastAttemptAt":"2026-11-01T15:57:20.8239354+00:00","lastSuccessAt":"2026-11-01T15:57:21.0432994+00:00","error":null,"lastVerifiedAt":"2026-11-01T15:57:21.0433001+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-aece262bb10a694367a43c24d730aa8e8dc171723eaec4c15cdd32f04bda103f-f95a49f7991d84fda391d9ec67b41d459e583b1cfe7c441477d04d1fe019c90a","complete":true,"records":417656,"lastAttemptAt":"2026-11-01T15:57:20.2988428+00:00","lastSuccessAt":"2026-11-01T15:57:20.2714671+00:00","error":null,"lastVerifiedAt":"2026-11-01T15:57:20.2988428+00:00","snapshotPublishedAt":"2026-11-01T15:25:59+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-996acc6ac5ed4512972e5675464bd820","complete":false,"records":13568,"lastAttemptAt":"2026-11-01T16:09:37.8504098+00:00","lastSuccessAt":"2026-11-01T16:09:37.846+00:00","error":null,"lastVerifiedAt":"2026-11-01T16:09:37.846+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13449,"expectedIdentifiers":13637,"failedIdentifiers":0},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-01T15:57:20.8239354+00:00","complete":true,"records":1095,"lastAttemptAt":"2026-11-01T15:57:20.8239354+00:00","lastSuccessAt":"2026-11-01T15:57:21.0444496+00:00","error":null,"lastVerifiedAt":"2026-11-01T15:57:21.0444501+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-f67819536b464ba998dbfc5a485cc684","complete":true,"records":13580,"lastAttemptAt":"2026-11-01T15:57:46.3017893+00:00","lastSuccessAt":"2026-11-01T15:57:46.298+00:00","error":null,"lastVerifiedAt":"2026-11-01T15:57:46.298+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13637,"expectedIdentifiers":13637,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-21dcd6662970dfea02e4ea60ee637115e8d92664947484d2b9276b3f6568a84f","complete":true,"records":9815,"lastAttemptAt":"2026-11-01T15:57:20.6646449+00:00","lastSuccessAt":"2026-10-30T23:47:34.1817587+00:00","error":null,"lastVerifiedAt":"2026-11-01T15:57:20.6646449+00:00","snapshotPublishedAt":"2026-10-30T23:33:17+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-2de9ccc5290f060c7aa63f89c25dd76f655a64ba1aba7c05c97fa3bee4f09899","complete":true,"records":1967,"lastAttemptAt":"2026-11-01T15:57:20.4527932+00:00","lastSuccessAt":"2026-10-28T20:55:34.2982937+00:00","error":null,"lastVerifiedAt":"2026-11-01T15:57:20.4527932+00:00","snapshotPublishedAt":"2026-10-28T20:03:08+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-f95f76cd406ae5bae3de632ad37fd2d5160b44f5e6b51748ac6f8f0c0c88e72b","complete":true,"records":3028,"lastAttemptAt":"2026-11-01T15:57:20.7894926+00:00","lastSuccessAt":"2026-10-30T23:47:38.4914494+00:00","error":null,"lastVerifiedAt":"2026-11-01T15:57:20.7894926+00:00","snapshotPublishedAt":"2026-10-30T23:18:12+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-55acacd40a7aab88cf530cfddf98233a5d800cfc6bb1883653ecf5bee50f4426","complete":true,"records":230996,"lastAttemptAt":"2026-11-01T15:57:20.5571291+00:00","lastSuccessAt":"2026-11-01T09:06:49.6961926+00:00","error":null,"lastVerifiedAt":"2026-11-01T15:57:20.5571291+00:00","snapshotPublishedAt":"2026-11-01T08:33:39+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}