middleware.ts 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import type { NextRequest } from 'next/server'
  2. import { NextResponse } from 'next/server'
  3. const NECESSARY_DOMAIN = '*.sentry.io http://localhost:* http://127.0.0.1:* https://analytics.google.com googletagmanager.com *.googletagmanager.com https://www.google-analytics.com https://api.github.com'
  4. export function middleware(request: NextRequest) {
  5. const isWhiteListEnabled = !!process.env.NEXT_PUBLIC_CSP_WHITELIST && process.env.NODE_ENV === 'production'
  6. if (!isWhiteListEnabled)
  7. return NextResponse.next()
  8. const whiteList = `${process.env.NEXT_PUBLIC_CSP_WHITELIST} ${NECESSARY_DOMAIN}`
  9. const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
  10. const csp = `'nonce-${nonce}'`
  11. const scheme_source = 'data: mediastream: blob: filesystem:'
  12. const cspHeader = `
  13. default-src 'self' ${scheme_source} ${csp} ${whiteList};
  14. connect-src 'self' ${scheme_source} ${csp} ${whiteList};
  15. script-src 'self' ${scheme_source} ${csp} ${whiteList};
  16. style-src 'self' 'unsafe-inline' ${scheme_source} ${whiteList};
  17. worker-src 'self' ${scheme_source} ${csp} ${whiteList};
  18. media-src 'self' ${scheme_source} ${csp} ${whiteList};
  19. img-src 'self' ${scheme_source} ${csp} ${whiteList};
  20. font-src 'self';
  21. object-src 'none';
  22. base-uri 'self';
  23. form-action 'self';
  24. upgrade-insecure-requests;
  25. `
  26. // Replace newline characters and spaces
  27. const contentSecurityPolicyHeaderValue = cspHeader
  28. .replace(/\s{2,}/g, ' ')
  29. .trim()
  30. const requestHeaders = new Headers(request.headers)
  31. requestHeaders.set('x-nonce', nonce)
  32. requestHeaders.set(
  33. 'Content-Security-Policy',
  34. contentSecurityPolicyHeaderValue,
  35. )
  36. const response = NextResponse.next({
  37. request: {
  38. headers: requestHeaders,
  39. },
  40. })
  41. response.headers.set(
  42. 'Content-Security-Policy',
  43. contentSecurityPolicyHeaderValue,
  44. )
  45. return response
  46. }
  47. export const config = {
  48. matcher: [
  49. /*
  50. * Match all request paths except for the ones starting with:
  51. * - api (API routes)
  52. * - _next/static (static files)
  53. * - _next/image (image optimization files)
  54. * - favicon.ico (favicon file)
  55. */
  56. {
  57. // source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
  58. source: '/((?!_next/static|_next/image|favicon.ico).*)',
  59. // source: '/(.*)',
  60. // missing: [
  61. // { type: 'header', key: 'next-router-prefetch' },
  62. // { type: 'header', key: 'purpose', value: 'prefetch' },
  63. // ],
  64. },
  65. ],
  66. }