mirror of
https://github.com/vacp2p/linea-monorepo.git
synced 2026-01-08 03:43:56 -05:00
125 lines
4.6 KiB
TypeScript
125 lines
4.6 KiB
TypeScript
// NextJS automatically recognises a single middleware.ts file in the project root - https://nextjs.org/docs/app/building-your-application/routing/middleware#convention
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
|
|
export function middleware(request: NextRequest) {
|
|
const nonce = Buffer.from(crypto.randomUUID()).toString("base64");
|
|
|
|
// We only want to allow unsafe-eval in local environment for NextJS dev server
|
|
// We are required to use unsafe-inline with Cloudflare - https://developers.cloudflare.com/fundamentals/reference/policies-compliances/content-security-policies/#product-requirements
|
|
// TODO: Remove unsafe-eval in production and replace with 'unsafe-inline'
|
|
// const unsafeScript = process.env.NEXT_PUBLIC_ENVIRONMENT === "local" ? "'unsafe-eval'" : "'unsafe-eval'";
|
|
|
|
/**
|
|
* Content Security Policy (CSP) configuration:
|
|
*
|
|
* default-src 'self'
|
|
* - Fallback policy to only allow resources from the same origin, unless overriden by a more specific policy
|
|
*
|
|
* script-src-elem 'self' 'nonce-{nonce}' 'strict-dynamic'
|
|
* - Allow scripts from the same origin, with the provided nonce, and child scripts recursively loaded from a script with nonce
|
|
*
|
|
* style-src 'self' 'unsafe-inline'
|
|
* - Allow styles from the same origin and inline styles.
|
|
*
|
|
* img-src
|
|
* - Control image source
|
|
* - We allow `https:` here because we cannot sustainably maintain a whitelist for token image sources used by our widgets, especially when new tokens come out everyday and some introduce new image sources
|
|
*
|
|
* font-src
|
|
* - Control font source
|
|
*
|
|
* connect-src
|
|
* - Controls all outbound network requests, including fetch(), WebSockets, EventSource, navigator.sendBeacon
|
|
*
|
|
* frame-src
|
|
* - Control source for iframes
|
|
*
|
|
* object-src 'none'
|
|
* - Disallow object, embed, and applet elements (should not appear in modern frontends)
|
|
*
|
|
* base-uri 'self'
|
|
* - Control <base href="..."> to be from same origin as the page
|
|
*
|
|
* form-action 'self'
|
|
* - Restrict form submissions to the same origin.
|
|
*
|
|
* frame-ancestors 'none'
|
|
* - Disallow this site from being embedded in iframes (similar to X-Frame-Options: DENY).
|
|
*
|
|
* block-all-mixed-content
|
|
* - Block all mixed (HTTP over HTTPS) content.
|
|
*
|
|
* upgrade-insecure-requests
|
|
* - Automatically upgrade HTTP requests to HTTPS.
|
|
*/
|
|
const cspHeader = `
|
|
default-src 'self';
|
|
script-src 'self' 'nonce-${nonce}' https://www.googletagmanager.com/gtm.js https://widget.intercom.io/widget/h5zisg78 https://ajax.cloudflare.com https://js.intercomcdn.com;
|
|
style-src 'self' 'unsafe-inline';
|
|
img-src 'self' blob: data: https:;
|
|
font-src 'self' data: https://cdn.jsdelivr.net;
|
|
connect-src 'self' https:;
|
|
frame-src 'self'
|
|
https://*.walletconnect.com
|
|
https://buy.onramper.com/;
|
|
object-src 'none';
|
|
base-uri 'self';
|
|
form-action 'self';
|
|
frame-ancestors 'none';
|
|
block-all-mixed-content;
|
|
upgrade-insecure-requests;
|
|
`;
|
|
|
|
/**
|
|
* Purposely excluded URLs from CSP whitelist because they seem suspicious
|
|
*
|
|
* base-uri
|
|
* - https://d6tizftlrpuof.cloudfront.net/live/
|
|
*
|
|
* script-src
|
|
* - https://snap.licdn.com
|
|
*/
|
|
|
|
// Replace newline characters and spaces
|
|
const contentSecurityPolicyHeaderValue = cspHeader.replace(/\s{2,}/g, " ").trim();
|
|
|
|
const requestHeaders = new Headers(request.headers);
|
|
// Pass nonce to <Script> elements in layout.tsx to bypass CSP
|
|
requestHeaders.set("x-nonce", nonce);
|
|
requestHeaders.set("Content-Security-Policy", contentSecurityPolicyHeaderValue);
|
|
// Set response headers so that browsers enforce CSP
|
|
const responseHeaders = new Headers();
|
|
responseHeaders.set("Content-Security-Policy", contentSecurityPolicyHeaderValue);
|
|
|
|
const response = NextResponse.next({
|
|
request: {
|
|
headers: requestHeaders,
|
|
},
|
|
});
|
|
response.headers.set("Content-Security-Policy", contentSecurityPolicyHeaderValue);
|
|
|
|
return response;
|
|
}
|
|
|
|
// Filter Middleware to run on specific paths - https://nextjs.org/docs/14/app/building-your-application/configuring/content-security-policy#adding-a-nonce-with-middleware
|
|
export const config = {
|
|
matcher: [
|
|
/*
|
|
* Match all request paths except:
|
|
* - api (API routes)
|
|
* - _next/static (static files)
|
|
* - _next/image (image optimization files)
|
|
* - favicon.ico (favicon file)
|
|
* - public folder
|
|
*/
|
|
{
|
|
source: "/((?!api|_next/static|_next/image|favicon.ico|public/).*)",
|
|
// Skip running Middleware if request includes NextJS prefetch headers
|
|
missing: [
|
|
{ type: "header", key: "next-router-prefetch" },
|
|
{ type: "header", key: "purpose", value: "prefetch" },
|
|
],
|
|
},
|
|
],
|
|
};
|