| 108 | // Security Headers |
| 109 | // --------------------------------------------------------------------------- |
| 110 | function buildCsp(): string { |
| 111 | const isDev = process.env.NODE_ENV !== "production"; |
| 112 | // Production Next.js still emits small inline bootstraps; Cloudflare proxy injects Insights. |
| 113 | const scriptSrc = isDev |
| 114 | ? "script-src 'self' 'unsafe-eval' 'unsafe-inline'" |
| 115 | : "script-src 'self' 'unsafe-inline' https://static.cloudflareinsights.com"; |
| 116 | |
| 117 | return [ |
| 118 | "default-src 'self'", |
| 119 | scriptSrc, |
| 120 | "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com", |
| 121 | "img-src 'self' data: blob: https: http:", |
| 122 | "font-src 'self' data: https://fonts.gstatic.com", |
| 123 | "connect-src 'self' https://polymarket.com https://gamma-api.polymarket.com https://data-api.polymarket.com https://clob.polymarket.com https://*.publicnode.com https://polygon-rpc.com https://1rpc.io https://rpc.ankr.com https://*.zan.top https://*.basemaps.cartocdn.com https://basemaps.cartocdn.com https://cdn.jsdelivr.net https://manifest.googlevideo.com https://*.googlevideo.com", |
| 124 | "media-src 'self' blob: https://*.googlevideo.com https://*.akamaized.net https://*.amagi.tv https://*.trt.com.tr https://*.nhkworld.jp", |
| 125 | "worker-src 'self' blob:", |
| 126 | "child-src 'self' blob:", |
| 127 | "frame-src 'self' https://www.youtube.com https://www.youtube-nocookie.com", |
| 128 | "frame-ancestors 'none'", |
| 129 | ].join("; "); |
| 130 | } |
| 131 | |
| 132 | const SECURITY_HEADERS: Record<string, string> = { |
| 133 | "X-Frame-Options": "DENY", |