| 
									
										
										
										
											2024-10-11 16:14:56 +08:00
										 |  |  | import type { NextRequest } from 'next/server' | 
					
						
							|  |  |  | import { NextResponse } from 'next/server' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-12 11:32:40 +08:00
										 |  |  | 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' | 
					
						
							| 
									
										
										
										
											2024-10-11 16:14:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-22 16:48:45 +08:00
										 |  |  | const wrapResponseWithXFrameOptions = (response: NextResponse, pathname: string) => { | 
					
						
							|  |  |  |   // prevent clickjacking: https://owasp.org/www-community/attacks/Clickjacking
 | 
					
						
							|  |  |  |   // Chatbot page should be allowed to be embedded in iframe. It's a feature
 | 
					
						
							| 
									
										
										
										
											2025-05-20 12:07:50 +08:00
										 |  |  |   if (process.env.NEXT_PUBLIC_ALLOW_EMBED !== 'true' && !pathname.startsWith('/chat') && !pathname.startsWith('/workflow') && !pathname.startsWith('/completion') && !pathname.startsWith('/webapp-signin')) | 
					
						
							| 
									
										
										
										
											2025-04-22 16:48:45 +08:00
										 |  |  |     response.headers.set('X-Frame-Options', 'DENY') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return response | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-10-11 16:14:56 +08:00
										 |  |  | export function middleware(request: NextRequest) { | 
					
						
							| 
									
										
										
										
											2025-04-22 16:48:45 +08:00
										 |  |  |   const { pathname } = request.nextUrl | 
					
						
							|  |  |  |   const requestHeaders = new Headers(request.headers) | 
					
						
							|  |  |  |   const response = NextResponse.next({ | 
					
						
							|  |  |  |     request: { | 
					
						
							|  |  |  |       headers: requestHeaders, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-11 16:14:56 +08:00
										 |  |  |   const isWhiteListEnabled = !!process.env.NEXT_PUBLIC_CSP_WHITELIST && process.env.NODE_ENV === 'production' | 
					
						
							|  |  |  |   if (!isWhiteListEnabled) | 
					
						
							| 
									
										
										
										
											2025-04-22 16:48:45 +08:00
										 |  |  |     return wrapResponseWithXFrameOptions(response, pathname) | 
					
						
							| 
									
										
										
										
											2024-10-11 16:14:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const whiteList = `${process.env.NEXT_PUBLIC_CSP_WHITELIST} ${NECESSARY_DOMAIN}` | 
					
						
							|  |  |  |   const nonce = Buffer.from(crypto.randomUUID()).toString('base64') | 
					
						
							|  |  |  |   const csp = `'nonce-${nonce}'` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const scheme_source = 'data: mediastream: blob: filesystem:' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const cspHeader = `
 | 
					
						
							|  |  |  |     default-src 'self' ${scheme_source} ${csp} ${whiteList}; | 
					
						
							|  |  |  |     connect-src 'self' ${scheme_source} ${csp} ${whiteList}; | 
					
						
							|  |  |  |     script-src 'self' ${scheme_source} ${csp} ${whiteList}; | 
					
						
							|  |  |  |     style-src 'self' 'unsafe-inline' ${scheme_source} ${whiteList}; | 
					
						
							|  |  |  |     worker-src 'self' ${scheme_source} ${csp} ${whiteList}; | 
					
						
							|  |  |  |     media-src 'self' ${scheme_source} ${csp} ${whiteList}; | 
					
						
							| 
									
										
										
										
											2025-05-17 10:55:12 +08:00
										 |  |  |     img-src * data: blob:; | 
					
						
							| 
									
										
										
										
											2024-10-11 16:14:56 +08:00
										 |  |  |     font-src 'self'; | 
					
						
							|  |  |  |     object-src 'none'; | 
					
						
							|  |  |  |     base-uri 'self'; | 
					
						
							|  |  |  |     form-action 'self'; | 
					
						
							|  |  |  |     upgrade-insecure-requests; | 
					
						
							|  |  |  | `
 | 
					
						
							|  |  |  |   // Replace newline characters and spaces
 | 
					
						
							|  |  |  |   const contentSecurityPolicyHeaderValue = cspHeader | 
					
						
							|  |  |  |     .replace(/\s{2,}/g, ' ') | 
					
						
							|  |  |  |     .trim() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   requestHeaders.set('x-nonce', nonce) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   requestHeaders.set( | 
					
						
							|  |  |  |     'Content-Security-Policy', | 
					
						
							|  |  |  |     contentSecurityPolicyHeaderValue, | 
					
						
							|  |  |  |   ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   response.headers.set( | 
					
						
							|  |  |  |     'Content-Security-Policy', | 
					
						
							|  |  |  |     contentSecurityPolicyHeaderValue, | 
					
						
							|  |  |  |   ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-22 16:48:45 +08:00
										 |  |  |   return wrapResponseWithXFrameOptions(response, pathname) | 
					
						
							| 
									
										
										
										
											2024-10-11 16:14:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const config = { | 
					
						
							|  |  |  |   matcher: [ | 
					
						
							|  |  |  |     /* | 
					
						
							|  |  |  |      * Match all request paths except for the ones starting with: | 
					
						
							|  |  |  |      * - api (API routes) | 
					
						
							|  |  |  |      * - _next/static (static files) | 
					
						
							|  |  |  |      * - _next/image (image optimization files) | 
					
						
							|  |  |  |      * - favicon.ico (favicon file) | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       // source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
 | 
					
						
							|  |  |  |       source: '/((?!_next/static|_next/image|favicon.ico).*)', | 
					
						
							|  |  |  |       // source: '/(.*)',
 | 
					
						
							|  |  |  |       // missing: [
 | 
					
						
							|  |  |  |       //   { type: 'header', key: 'next-router-prefetch' },
 | 
					
						
							|  |  |  |       //   { type: 'header', key: 'purpose', value: 'prefetch' },
 | 
					
						
							|  |  |  |       // ],
 | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   ], | 
					
						
							|  |  |  | } |