Skip to content

Commit bcadd23

Browse files
authored
Fix middleware bypass (#810)
* fix middleware bypass * Create bright-balloons-own.md
1 parent 1471a52 commit bcadd23

File tree

4 files changed

+62
-1
lines changed

4 files changed

+62
-1
lines changed

.changeset/bright-balloons-own.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@opennextjs/aws": patch
3+
---
4+
5+
Fix a security vulnerability similar to the recent CVE-2025-29927

packages/open-next/src/core/routing/middleware.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
FunctionsConfigManifest,
55
MiddlewareManifest,
66
NextConfig,
7+
PrerenderManifest,
78
} from "config/index.js";
89
import type { InternalEvent, InternalResult } from "types/open-next.js";
910
import { emptyReadableStream } from "utils/stream.js";
@@ -57,7 +58,14 @@ export async function handleMiddleware(
5758
const headers = internalEvent.headers;
5859

5960
// We bypass the middleware if the request is internal
60-
if (headers["x-isr"]) return internalEvent;
61+
// We should only do that if the request has the correct `x-prerender-revalidate` header
62+
// The `x-prerender-revalidate` header is set at build time and should be safe to trust
63+
if (
64+
headers["x-isr"] &&
65+
headers["x-prerender-revalidate"] ===
66+
PrerenderManifest.preview.previewModeId
67+
)
68+
return internalEvent;
6169

6270
// We only need the normalizedPath to check if the middleware should run
6371
const normalizedPath = localizePath(internalEvent);

packages/open-next/src/types/next-types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ export interface PrerenderManifest {
174174
dataRouteRegex: string;
175175
};
176176
};
177+
preview: {
178+
previewModeId: string;
179+
};
177180
}
178181

179182
export type Options = {

packages/tests-unit/tests/core/routing/middleware.test.ts

+45
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ vi.mock("@opennextjs/aws/adapters/config/index.js", () => ({
3333
version: 2,
3434
},
3535
FunctionsConfigManifest: undefined,
36+
PrerenderManifest: {
37+
preview: {
38+
previewModeId: "preview",
39+
},
40+
},
3641
}));
3742

3843
vi.mock("@opennextjs/aws/core/routing/i18n/index.js", () => ({
@@ -76,6 +81,7 @@ describe("handleMiddleware", () => {
7681
const event = createEvent({
7782
headers: {
7883
"x-isr": "1",
84+
"x-prerender-revalidate": "preview",
7985
},
8086
});
8187
const result = await handleMiddleware(event, middlewareLoader);
@@ -84,6 +90,45 @@ describe("handleMiddleware", () => {
8490
expect(result).toEqual(event);
8591
});
8692

93+
it("should not bypass middleware for request with an incorrect x-prerender-revalidate", async () => {
94+
const event = createEvent({
95+
headers: {
96+
"x-isr": "1",
97+
"x-prerender-revalidate": "incorrect",
98+
},
99+
});
100+
middleware.mockResolvedValue({
101+
status: 302,
102+
headers: new Headers({
103+
location: "/redirect",
104+
}),
105+
});
106+
const result = await handleMiddleware(event, middlewareLoader);
107+
108+
expect(middlewareLoader).toHaveBeenCalled();
109+
expect(result.statusCode).toEqual(302);
110+
expect(result.headers.location).toEqual("/redirect");
111+
});
112+
113+
it("should not bypass middleware if there is no x-prerender-revalidate", async () => {
114+
const event = createEvent({
115+
headers: {
116+
"x-isr": "1",
117+
},
118+
});
119+
middleware.mockResolvedValue({
120+
status: 302,
121+
headers: new Headers({
122+
location: "/redirect",
123+
}),
124+
});
125+
const result = await handleMiddleware(event, middlewareLoader);
126+
127+
expect(middlewareLoader).toHaveBeenCalled();
128+
expect(result.statusCode).toEqual(302);
129+
expect(result.headers.location).toEqual("/redirect");
130+
});
131+
87132
it("should invoke middleware with redirect", async () => {
88133
const event = createEvent({});
89134
middleware.mockResolvedValue({

0 commit comments

Comments
 (0)