import { env, getEnv } from '../config/env' import { isDev, isHosted, isReactGrabEnabled } from '../config/env-flags' /** * Content Security Policy (CSP) configuration builder * * NOTE: This file is loaded by next.config.ts at build time, before @/ path * aliases are resolved. Do NOT import from ../utils/urls (which uses @/ imports). * Keep all URL constants local to this file. */ const DEFAULT_SOCKET_URL = 'http://localhost:3002' const DEFAULT_OLLAMA_URL = 'http://localhost:11434' function toWebSocketUrl(httpUrl: string): string { return httpUrl.replace('http://', 'ws://').replace('https://', 'wss://') } function getHostnameFromUrl(url: string | undefined): string[] { if (!url) return [] try { return [`https://${new URL(url).hostname}`] } catch { return [] } } export interface CSPDirectives { 'default-src'?: string[] 'script-src'?: string[] 'style-src'?: string[] 'img-src'?: string[] 'media-src'?: string[] 'font-src'?: string[] 'connect-src'?: string[] 'worker-src'?: string[] 'frame-src'?: string[] 'frame-ancestors'?: string[] 'form-action'?: string[] 'base-uri'?: string[] 'object-src'?: string[] } /** * Static CSP sources shared between build-time and runtime. * Add new domains here — both paths pick them up automatically. */ const STATIC_SCRIPT_SRC = [ "'self'", "'unsafe-inline'", ...(isDev ? ["'unsafe-eval'"] : []), 'https://*.google.com', 'https://apis.google.com', 'https://challenges.cloudflare.com', // Cal.com booking embed (landing /demo) — embed.js is served from app.cal.com 'https://app.cal.com', ...(isReactGrabEnabled ? ['https://unpkg.com'] : []), ...(isHosted ? [ 'https://www.googletagmanager.com', 'https://www.google-analytics.com', 'https://analytics.ahrefs.com', // HubSpot tracking (landing pages) — loader plus the // analytics/form-tracking/banner scripts it injects as