From 3d27e847737f76e76f4cd839666fb199968711ad Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Sat, 28 Feb 2026 11:24:38 +0100 Subject: [PATCH] fix: preserve query string when stripping prefix from req.url normalizePathForMatching() was applied to the full URL including the query string, but FindMyWay.sanitizeUrlPath() strips query parameters as part of path sanitization. Now the query string is extracted before normalization and re-appended afterward. Closes #248 Co-Authored-By: Claude Opus 4.6 --- lib/engine.js | 7 +++++-- test/req-url-stripping.test.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/engine.js b/lib/engine.js index 162536e..1fd290a 100644 --- a/lib/engine.js +++ b/lib/engine.js @@ -63,8 +63,11 @@ function middie (complete, options = {}) { const holder = pool.get() holder.req = req holder.res = res - holder.normalizedUrl = normalizePathForMatching(sanitizeUrl(req.url), normalizationOptions) - holder.normalizedReqUrl = normalizePathForMatching(req.url, normalizationOptions) + const sanitizedUrl = sanitizeUrl(req.url) + const queryString = req.url.slice(sanitizedUrl.length) + const normalizedPath = normalizePathForMatching(sanitizedUrl, normalizationOptions) + holder.normalizedUrl = normalizedPath + holder.normalizedReqUrl = normalizedPath + queryString holder.context = ctx holder.done() } diff --git a/test/req-url-stripping.test.js b/test/req-url-stripping.test.js index 02518fa..7a5226f 100644 --- a/test/req-url-stripping.test.js +++ b/test/req-url-stripping.test.js @@ -112,6 +112,37 @@ test('req.url stripping with trailing slash', async (t) => { t.assert.strictEqual(capturedUrl, '/data', '/secret/data/ should strip to /data') }) +test('req.url stripping preserves query string', async (t) => { + const app = Fastify() + t.after(() => app.close()) + + await app.register(middiePlugin) + + let capturedUrl = null + + app.use('/api', (req, _res, next) => { + capturedUrl = req.url + next() + }) + + app.get('/api/resource', async () => ({ ok: true })) + + // Query string should be preserved after prefix stripping + capturedUrl = null + await app.inject({ method: 'GET', url: '/api/resource?foo=bar&baz=qux' }) + t.assert.strictEqual(capturedUrl, '/resource?foo=bar&baz=qux', 'query string should be preserved') + + // Simple query string + capturedUrl = null + await app.inject({ method: 'GET', url: '/api/resource?key=value' }) + t.assert.strictEqual(capturedUrl, '/resource?key=value', 'simple query string should be preserved') + + // No query string should still work + capturedUrl = null + await app.inject({ method: 'GET', url: '/api/resource' }) + t.assert.strictEqual(capturedUrl, '/resource', 'no query string should work normally') +}) + test('req.url stripping with all normalization options combined', async (t) => { const app = Fastify({ routerOptions: {