Skip to content

Commit f72b6a2

Browse files
conico974Nicolas Dorseuil
andauthored
Validate 'next-action' header format in server action requests (#3523)
Co-authored-by: Nicolas Dorseuil <[email protected]>
1 parent 2f79820 commit f72b6a2

File tree

1 file changed

+24
-10
lines changed

1 file changed

+24
-10
lines changed

packages/gitbook/src/middleware.ts

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,21 +64,35 @@ export async function middleware(request: NextRequest) {
6464
}
6565

6666
async function validateServerActionRequest(request: NextRequest) {
67-
// We need to reject incorrect server actions requests
68-
// We do not do it in cloudflare workers as there is a bug that prevents us from reading the request body.
69-
if (request.headers.has('next-action') && process.env.GITBOOK_RUNTIME !== 'cloudflare') {
70-
// We just test that the json body is parseable
71-
try {
72-
const clonedRequest = request.clone();
73-
await clonedRequest.json();
74-
} catch (e) {
75-
console.warn('Invalid server action request', e);
76-
// If the body is not parseable, we reject the request
67+
// First thing we need to do is validate that the header is in a correct format.
68+
if (request.headers.has('next-action')) {
69+
// A server action id is a 1-byte hex string (2 chars) followed by a 20-byte SHA1 hash (40 chars) = 42 total characters.
70+
// For ref https://github.com/vercel/next.js/blob/db561cb924cbea0f3384e89f251fc443a8aec1ae/crates/next-custom-transforms/src/transforms/server_actions.rs#L266-L268
71+
const regex = /^[a-fA-F0-9]{42}$/;
72+
const match = request.headers.get('next-action')?.match(regex);
73+
if (!match) {
7774
return new Response('Invalid request', {
7875
status: 400,
7976
headers: { 'content-type': 'text/plain' },
8077
});
8178
}
79+
80+
// We need to reject incorrect server actions requests
81+
// We do not do it in cloudflare workers as there is a bug that prevents us from reading the request body.
82+
if (process.env.GITBOOK_RUNTIME !== 'cloudflare') {
83+
// We just test that the json body is parseable
84+
try {
85+
const clonedRequest = request.clone();
86+
await clonedRequest.json();
87+
} catch (e) {
88+
console.warn('Invalid server action request', e);
89+
// If the body is not parseable, we reject the request
90+
return new Response('Invalid request', {
91+
status: 400,
92+
headers: { 'content-type': 'text/plain' },
93+
});
94+
}
95+
}
8296
}
8397
}
8498

0 commit comments

Comments
 (0)