UNPKG

@fastify/middie

Version:

Middleware engine for Fastify

235 lines (178 loc) 7.13 kB
'use strict' const { test } = require('node:test') const Fastify = require('fastify') const middiePlugin = require('../index') test('req.url stripping preserves percent-encoded characters', async (t) => { const app = Fastify() t.after(() => app.close()) await app.register(middiePlugin) let capturedUrl = null app.use('/prefix', (req, _res, next) => { capturedUrl = req.url next() }) app.get('/prefix/*', async () => ({ ok: true })) capturedUrl = null await app.inject({ method: 'GET', url: '/prefix/hello%20world' }) t.assert.strictEqual(capturedUrl, '/hello%20world', 'percent-encoded space preserved') capturedUrl = null await app.inject({ method: 'GET', url: '/prefix/hello%20world%2Ffoo' }) t.assert.strictEqual(capturedUrl, '/hello%20world%2Ffoo', 'percent-encoded slash preserved') capturedUrl = null await app.inject({ method: 'GET', url: '/prefix/path%2Fwith%2Fslashes' }) t.assert.strictEqual(capturedUrl, '/path%2Fwith%2Fslashes', 'multiple percent-encoded slashes preserved') capturedUrl = null await app.inject({ method: 'GET', url: '/prefix/%E4%B8%AD%E6%96%87' }) t.assert.strictEqual(capturedUrl, '/%E4%B8%AD%E6%96%87', 'percent-encoded unicode preserved') }) test('req.url stripping with duplicate slashes', async (t) => { const app = Fastify({ routerOptions: { ignoreDuplicateSlashes: true } }) t.after(() => app.close()) await app.register(middiePlugin) let capturedUrl = null app.use('/secret', (req, _res, next) => { capturedUrl = req.url next() }) app.get('/secret/data', async () => ({ ok: true })) // Normal case capturedUrl = null await app.inject({ method: 'GET', url: '/secret/data' }) t.assert.strictEqual(capturedUrl, '/data', 'normal path should strip to /data') // Double slash before - should normalize and strip correctly capturedUrl = null await app.inject({ method: 'GET', url: '//secret/data' }) t.assert.strictEqual(capturedUrl, '/data', '//secret/data should strip to /data, not //data') // Double slash after prefix - should normalize and strip correctly capturedUrl = null await app.inject({ method: 'GET', url: '/secret//data' }) t.assert.strictEqual(capturedUrl, '/data', '/secret//data should strip to /data, not //data') }) test('req.url stripping with semicolon delimiter', async (t) => { const app = Fastify({ routerOptions: { useSemicolonDelimiter: true } }) t.after(() => app.close()) await app.register(middiePlugin) let capturedUrl = null app.use('/secret', (req, _res, next) => { capturedUrl = req.url next() }) app.get('/secret', async () => ({ ok: true })) app.get('/secret/data', async () => ({ ok: true })) // Normal case capturedUrl = null await app.inject({ method: 'GET', url: '/secret' }) t.assert.strictEqual(capturedUrl, '/', 'normal path should strip to /') // Semicolon variant - should normalize and strip correctly capturedUrl = null await app.inject({ method: 'GET', url: '/secret;foo=bar' }) t.assert.strictEqual(capturedUrl, '/', '/secret;foo=bar should strip to /, not /;foo=bar') // Semicolon with path after - note: semicolon delimiter treats everything after ; as params // so /secret;foo=bar/data is path=/secret with params, not path=/secret/data capturedUrl = null await app.inject({ method: 'GET', url: '/secret;foo=bar/data' }) t.assert.strictEqual(capturedUrl, '/', '/secret;foo=bar/data has path /secret, strips to /') }) test('req.url stripping with trailing slash', async (t) => { const app = Fastify({ routerOptions: { ignoreTrailingSlash: true } }) t.after(() => app.close()) await app.register(middiePlugin) let capturedUrl = null app.use('/secret', (req, _res, next) => { capturedUrl = req.url next() }) app.get('/secret', async () => ({ ok: true })) app.get('/secret/data', async () => ({ ok: true })) // Normal case capturedUrl = null await app.inject({ method: 'GET', url: '/secret' }) t.assert.strictEqual(capturedUrl, '/', 'normal path should strip to /') // Trailing slash variant capturedUrl = null await app.inject({ method: 'GET', url: '/secret/' }) t.assert.strictEqual(capturedUrl, '/', '/secret/ should strip to /') // With subpath and trailing slash capturedUrl = null await app.inject({ method: 'GET', url: '/secret/data/' }) t.assert.strictEqual(capturedUrl, '/data', '/secret/data/ should strip to /data') }) test('req.url stripping with all normalization options combined', async (t) => { const app = Fastify({ routerOptions: { ignoreDuplicateSlashes: true, useSemicolonDelimiter: true, ignoreTrailingSlash: true } }) t.after(() => app.close()) await app.register(middiePlugin) let capturedUrl = null app.use('/secret', (req, _res, next) => { capturedUrl = req.url next() }) app.get('/secret', async () => ({ ok: true })) app.get('/secret/data', async () => ({ ok: true })) // Complex case combining multiple normalizations capturedUrl = null await app.inject({ method: 'GET', url: '//secret;foo=bar/' }) t.assert.strictEqual(capturedUrl, '/', '//secret;foo=bar/ should strip to /') capturedUrl = null await app.inject({ method: 'GET', url: '//secret//data//' }) 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 })) capturedUrl = null await app.inject({ method: 'GET', url: '/api/resource?foo=bar' }) t.assert.strictEqual(capturedUrl, '/resource?foo=bar', 'single query param preserved') capturedUrl = null await app.inject({ method: 'GET', url: '/api/resource?foo=bar&baz=qux' }) t.assert.strictEqual(capturedUrl, '/resource?foo=bar&baz=qux', 'multiple query params preserved') capturedUrl = null await app.inject({ method: 'GET', url: '/api/resource?a=1&b=2&c=3' }) t.assert.strictEqual(capturedUrl, '/resource?a=1&b=2&c=3', 'many query params preserved') }) test('req.url stripping preserves query string with normalization options', async (t) => { const app = Fastify({ routerOptions: { ignoreDuplicateSlashes: true, ignoreTrailingSlash: true } }) t.after(() => app.close()) await app.register(middiePlugin) let capturedUrl = null app.use('/secret', (req, _res, next) => { capturedUrl = req.url next() }) app.get('/secret/data', async () => ({ ok: true })) capturedUrl = null await app.inject({ method: 'GET', url: '//secret/data?key=value' }) t.assert.strictEqual(capturedUrl, '/data?key=value', '//secret/data?key=value preserves query string') capturedUrl = null await app.inject({ method: 'GET', url: '/secret//data/?key=value' }) t.assert.strictEqual(capturedUrl, '/data?key=value', '/secret//data/?key=value preserves query string') })