mirror of
				https://github.com/langgenius/dify.git
				synced 2025-10-30 18:33:30 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			87 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			87 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import type { NextRequest } from 'next/server'
 | |
| import { NextResponse } from 'next/server'
 | |
| 
 | |
| 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'
 | |
| 
 | |
| 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
 | |
|   if (process.env.NEXT_PUBLIC_ALLOW_EMBED !== 'true' && !pathname.startsWith('/chat') && !pathname.startsWith('/workflow') && !pathname.startsWith('/completion') && !pathname.startsWith('/webapp-signin'))
 | |
|     response.headers.set('X-Frame-Options', 'DENY')
 | |
| 
 | |
|   return response
 | |
| }
 | |
| export function middleware(request: NextRequest) {
 | |
|   const { pathname } = request.nextUrl
 | |
|   const requestHeaders = new Headers(request.headers)
 | |
|   const response = NextResponse.next({
 | |
|     request: {
 | |
|       headers: requestHeaders,
 | |
|     },
 | |
|   })
 | |
| 
 | |
|   const isWhiteListEnabled = !!process.env.NEXT_PUBLIC_CSP_WHITELIST && process.env.NODE_ENV === 'production'
 | |
|   if (!isWhiteListEnabled)
 | |
|     return wrapResponseWithXFrameOptions(response, pathname)
 | |
| 
 | |
|   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};
 | |
|     img-src * data: blob:;
 | |
|     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,
 | |
|   )
 | |
| 
 | |
|   return wrapResponseWithXFrameOptions(response, pathname)
 | |
| }
 | |
| 
 | |
| 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' },
 | |
|       // ],
 | |
|     },
 | |
|   ],
 | |
| }
 | 
