Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7fe6b3b

Browse filesBrowse files
serhalpmrstork
andauthored
fix(edge-runtime): match path with URI-encoded chars (#2873)
Following the behaviour in vercel/next.js#78325, if a path matches a configured middleware matcher when URI-decoded, consider it a match. This doesn't seem ideal, but it aligns our behaviour with that of Next.js, and ensures that static asset matching behaviour aligns with edge middleware matching behaviour. Co-authored-by: Mateusz Bocian <mrstork@users.noreply.github.com>
1 parent 5dac3e3 commit 7fe6b3b
Copy full SHA for 7fe6b3b

File tree

Expand file treeCollapse file tree

10 files changed

+74
-5
lines changed
Filter options
Expand file treeCollapse file tree

10 files changed

+74
-5
lines changed

‎edge-runtime/lib/routing.ts

Copy file name to clipboardExpand all lines: edge-runtime/lib/routing.ts
+12-2Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -424,14 +424,24 @@ export interface MiddlewareMatcher {
424424
missing?: RouteHas[]
425425
}
426426

427+
const decodeMaybeEncodedPath = (path: string): string => {
428+
try {
429+
return decodeURIComponent(path)
430+
} catch {
431+
return path
432+
}
433+
}
434+
427435
export function getMiddlewareRouteMatcher(matchers: MiddlewareMatcher[]): MiddlewareRouteMatch {
428436
return (
429-
pathname: string | null | undefined,
437+
unsafePathname: string | null | undefined,
430438
req: Pick<Request, 'headers' | 'url'>,
431439
query: Params,
432440
) => {
441+
const pathname = decodeMaybeEncodedPath(unsafePathname ?? '')
442+
433443
for (const matcher of matchers) {
434-
const routeMatch = new RegExp(matcher.regexp).exec(pathname!)
444+
const routeMatch = new RegExp(matcher.regexp).exec(pathname)
435445
if (!routeMatch) {
436446
continue
437447
}

‎tests/e2e/edge-middleware.test.ts

Copy file name to clipboardExpand all lines: tests/e2e/edge-middleware.test.ts
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,15 @@ test("requests with x-middleware-subrequest don't skip middleware (GHSA-f82v-jwr
233233
expect(response.headers.get('x-test-used-next-version')).toBe('15.2.2')
234234
})
235235

236+
test('requests with different encoding than matcher match anyway', async ({
237+
middlewareStaticAssetMatcher,
238+
}) => {
239+
const response = await fetch(`${middlewareStaticAssetMatcher.url}/hello%2Fworld.txt`)
240+
241+
// middleware was not skipped
242+
expect(await response.text()).toBe('hello from middleware')
243+
})
244+
236245
test.describe('RSC cache poisoning', () => {
237246
test('Middleware rewrite', async ({ page, middleware }) => {
238247
const prefetchResponsePromise = new Promise<Response>((resolve) => {

‎tests/fixtures/middleware-og/package.json

Copy file name to clipboardExpand all lines: tests/fixtures/middleware-og/package.json
-2Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
"build": "next build"
99
},
1010
"dependencies": {
11-
"@aws-amplify/adapter-nextjs": "^1.0.18",
12-
"aws-amplify": "^6.0.18",
1311
"next": "latest",
1412
"react": "18.2.0",
1513
"react-dom": "18.2.0"
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export const metadata = {
2+
title: 'Simple Next App',
3+
description: 'Description for Simple Next App',
4+
}
5+
6+
export default function RootLayout({ children }) {
7+
return (
8+
<html lang="en">
9+
<body>{children}</body>
10+
</html>
11+
)
12+
}
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Home() {
2+
return (
3+
<main>
4+
<h1>Home</h1>
5+
</main>
6+
)
7+
}
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function middleware() {
2+
return new Response('hello from middleware')
3+
}
4+
5+
export const config = {
6+
matcher: '/hello/world.txt',
7+
}
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/** @type {import('next').NextConfig} */
2+
const nextConfig = {
3+
output: 'standalone',
4+
eslint: {
5+
ignoreDuringBuilds: true,
6+
},
7+
}
8+
9+
module.exports = nextConfig
+15Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "middleware",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"postinstall": "next build",
7+
"dev": "next dev",
8+
"build": "next build"
9+
},
10+
"dependencies": {
11+
"next": "latest",
12+
"react": "18.2.0",
13+
"react-dom": "18.2.0"
14+
}
15+
}
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
hello from a static asset

‎tests/utils/create-e2e-fixture.ts

Copy file name to clipboardExpand all lines: tests/utils/create-e2e-fixture.ts
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,10 +333,11 @@ export const fixtureFactories = {
333333
pnpm: () => createE2EFixture('pnpm', { packageManger: 'pnpm' }),
334334
bun: () => createE2EFixture('simple', { packageManger: 'bun' }),
335335
middleware: () => createE2EFixture('middleware'),
336-
middlewareSubrequestVuln: () => createE2EFixture('middleware-subrequest-vuln'),
337336
middlewareI18nExcludedPaths: () => createE2EFixture('middleware-i18n-excluded-paths'),
338337
middlewareOg: () => createE2EFixture('middleware-og'),
339338
middlewarePages: () => createE2EFixture('middleware-pages'),
339+
middlewareStaticAssetMatcher: () => createE2EFixture('middleware-static-asset-matcher'),
340+
middlewareSubrequestVuln: () => createE2EFixture('middleware-subrequest-vuln'),
340341
pageRouter: () => createE2EFixture('page-router'),
341342
pageRouterBasePathI18n: () => createE2EFixture('page-router-base-path-i18n'),
342343
turborepo: () =>

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.