diff --git a/packages/gitbook-v2/src/middleware.ts b/packages/gitbook-v2/src/middleware.ts index 4bcbf77aea..b346d5a74d 100644 --- a/packages/gitbook-v2/src/middleware.ts +++ b/packages/gitbook-v2/src/middleware.ts @@ -43,6 +43,12 @@ export async function middleware(request: NextRequest) { return NextResponse.redirect(normalized.toString()); } + // Reject malicious requests + const rejectResponse = await validateServerActionRequest(request); + if (rejectResponse) { + return rejectResponse; + } + for (const handler of [serveSiteRoutes, serveSpacePDFRoutes]) { const result = await handler(requestURL, request); if (result) { @@ -56,6 +62,25 @@ export async function middleware(request: NextRequest) { } } +async function validateServerActionRequest(request: NextRequest) { + // We need to reject incorrect server actions requests + // We do not do it in cloudflare workers as there is a bug that prevents us from reading the request body. + if (request.headers.has('next-action') && process.env.GITBOOK_RUNTIME !== 'cloudflare') { + // We just test that the json body is parseable + try { + const clonedRequest = request.clone(); + await clonedRequest.json(); + } catch (e) { + console.warn('Invalid server action request', e); + // If the body is not parseable, we reject the request + return new Response('Invalid request', { + status: 400, + headers: { 'content-type': 'text/plain' }, + }); + } + } +} + /** * Handle request that are targetting the site routes group. */ @@ -82,6 +107,14 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) { }); } + // We want to filter hostnames that contains a port here as this is likely a malicious request. + if (siteRequestURL.host.includes(':')) { + return new Response('Invalid request', { + status: 400, + headers: { 'content-type': 'text/plain' }, + }); + } + // // Detect and extract the visitor authentication token from the request //