55 lines
2.4 KiB
JavaScript
55 lines
2.4 KiB
JavaScript
import { NextResponse } from 'next/server';
|
|
|
|
function addSecurityHeaders(response) {
|
|
// Prevent MIME type sniffing (CWE-693)
|
|
response.headers.set('X-Content-Type-Options', 'nosniff');
|
|
// Prevent clickjacking (CWE-1021)
|
|
response.headers.set('X-Frame-Options', 'DENY');
|
|
// Enable XSS filter in legacy browsers
|
|
response.headers.set('X-XSS-Protection', '1; mode=block');
|
|
// Referrer policy
|
|
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
// Content Security Policy - restricts script sources to prevent XSS (CWE-79).
|
|
// connect-src covers *.muapi.ai (not just api.muapi.ai) because generated
|
|
// media, model thumbnails, and other assets are served from cdn.muapi.ai
|
|
// and other muapi subdomains that the renderer fetches directly.
|
|
response.headers.set(
|
|
'Content-Security-Policy',
|
|
"default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; media-src 'self' data: blob: https:; connect-src 'self' https://muapi.ai https://*.muapi.ai; font-src 'self' data:;"
|
|
);
|
|
return response;
|
|
}
|
|
|
|
export function middleware(request) {
|
|
const url = request.nextUrl;
|
|
|
|
// Catch requests to /api/workflow, /api/app, and /api/v1
|
|
const isMuApi = url.pathname.startsWith('/api/workflow') ||
|
|
url.pathname.startsWith('/api/app') ||
|
|
url.pathname.startsWith('/api/v1');
|
|
|
|
if (isMuApi) {
|
|
// Exclude paths that have their own dedicated route handlers with custom logic
|
|
const isHandledByRoute = url.pathname.startsWith('/api/v1/creative-agent') ||
|
|
url.pathname.startsWith('/api/v1/get_upload_url') ||
|
|
url.pathname.startsWith('/api/v1/upload-binary');
|
|
|
|
if (url.pathname.startsWith('/api/v1') && !isHandledByRoute) {
|
|
const targetUrl = new URL(url.pathname + url.search, 'https://api.muapi.ai');
|
|
const rewriteResponse = NextResponse.rewrite(targetUrl);
|
|
return addSecurityHeaders(rewriteResponse);
|
|
}
|
|
}
|
|
|
|
// Add security headers to all responses
|
|
return addSecurityHeaders(NextResponse.next());
|
|
}
|
|
|
|
// Match all paths for security headers. Exclude Next.js internal paths.
|
|
export const config = {
|
|
matcher: [
|
|
'/api/:path*',
|
|
'/((?!_next/static|_next/image|favicon.ico|__nextjs_original-stack-frame).*)',
|
|
],
|
|
};
|