1- import { env } from '../env'
1+ import crypto from 'crypto'
2+ import { env , getEnv } from '../env'
23
34/**
45 * Content Security Policy (CSP) configuration builder
@@ -20,7 +21,8 @@ export interface CSPDirectives {
2021 'object-src' ?: string [ ]
2122}
2223
23- export const cspDirectives : CSPDirectives = {
24+ // Build-time CSP directives (for next.config.ts)
25+ export const buildTimeCSPDirectives : CSPDirectives = {
2426 'default-src' : [ "'self'" ] ,
2527
2628 'script-src' : [
@@ -115,10 +117,60 @@ export function buildCSPString(directives: CSPDirectives): string {
115117}
116118
117119/**
118- * Get the main CSP policy string
120+ * Generate runtime CSP header with dynamic environment variables (safer approach)
121+ * This maintains compatibility with existing inline scripts while fixing Docker env var issues
122+ */
123+ export function generateRuntimeCSP ( ) : string {
124+ const socketUrl = getEnv ( 'NEXT_PUBLIC_SOCKET_URL' ) || 'http://localhost:3002'
125+ const socketWsUrl =
126+ socketUrl . replace ( 'http://' , 'ws://' ) . replace ( 'https://' , 'wss://' ) || 'ws://localhost:3002'
127+ const appUrl = getEnv ( 'NEXT_PUBLIC_APP_URL' ) || ''
128+ const ollamaUrl = getEnv ( 'OLLAMA_URL' ) || 'http://localhost:11434'
129+
130+ return `
131+ default-src 'self';
132+ script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.google.com https://apis.google.com https://*.vercel-scripts.com https://*.vercel-insights.com https://vercel.live https://*.vercel.live https://vercel.com https://*.vercel.app https://vitals.vercel-insights.com https://b2bjsstore.s3.us-west-2.amazonaws.com;
133+ style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
134+ img-src 'self' data: blob: https://*.googleusercontent.com https://*.google.com https://*.atlassian.com https://cdn.discordapp.com https://*.githubusercontent.com https://*.public.blob.vercel-storage.com;
135+ media-src 'self' blob:;
136+ font-src 'self' https://fonts.gstatic.com;
137+ connect-src 'self' ${ appUrl } ${ ollamaUrl } ${ socketUrl } ${ socketWsUrl } https://*.up.railway.app wss://*.up.railway.app https://api.browser-use.com https://api.exa.ai https://api.firecrawl.dev https://*.googleapis.com https://*.amazonaws.com https://*.s3.amazonaws.com https://*.blob.core.windows.net https://*.vercel-insights.com https://vitals.vercel-insights.com https://*.atlassian.com https://*.supabase.co https://vercel.live https://*.vercel.live https://vercel.com https://*.vercel.app wss://*.vercel.app https://pro.ip-api.com;
138+ frame-src https://drive.google.com https://docs.google.com https://*.google.com;
139+ frame-ancestors 'self';
140+ form-action 'self';
141+ base-uri 'self';
142+ object-src 'none';
143+ `
144+ . replace ( / \s { 2 , } / g, ' ' )
145+ . trim ( )
146+ }
147+
148+ /**
149+ * Get the main CSP policy string (build-time)
119150 */
120151export function getMainCSPPolicy ( ) : string {
121- return buildCSPString ( cspDirectives )
152+ return buildCSPString ( buildTimeCSPDirectives )
153+ }
154+
155+ /**
156+ * Generate a cryptographically secure nonce for CSP
157+ */
158+ export function generateNonce ( ) : string {
159+ return Buffer . from ( crypto . randomUUID ( ) ) . toString ( 'base64' )
160+ }
161+
162+ /**
163+ * Get the current request's nonce from headers (for use in app components)
164+ */
165+ export function getNonce ( ) : string | null {
166+ try {
167+ // This requires dynamic imports to avoid edge runtime issues
168+ const { headers } = require ( 'next/headers' )
169+ const headersList = headers ( )
170+ return headersList . get ( 'x-nonce' )
171+ } catch {
172+ return null
173+ }
122174}
123175
124176/**
@@ -129,22 +181,24 @@ export function getWorkflowExecutionCSPPolicy(): string {
129181}
130182
131183/**
132- * Add a source to a specific directive
184+ * Add a source to a specific directive (modifies build-time directives)
133185 */
134186export function addCSPSource ( directive : keyof CSPDirectives , source : string ) : void {
135- if ( ! cspDirectives [ directive ] ) {
136- cspDirectives [ directive ] = [ ]
187+ if ( ! buildTimeCSPDirectives [ directive ] ) {
188+ buildTimeCSPDirectives [ directive ] = [ ]
137189 }
138- if ( ! cspDirectives [ directive ] ! . includes ( source ) ) {
139- cspDirectives [ directive ] ! . push ( source )
190+ if ( ! buildTimeCSPDirectives [ directive ] ! . includes ( source ) ) {
191+ buildTimeCSPDirectives [ directive ] ! . push ( source )
140192 }
141193}
142194
143195/**
144- * Remove a source from a specific directive
196+ * Remove a source from a specific directive (modifies build-time directives)
145197 */
146198export function removeCSPSource ( directive : keyof CSPDirectives , source : string ) : void {
147- if ( cspDirectives [ directive ] ) {
148- cspDirectives [ directive ] = cspDirectives [ directive ] ! . filter ( ( s : string ) => s !== source )
199+ if ( buildTimeCSPDirectives [ directive ] ) {
200+ buildTimeCSPDirectives [ directive ] = buildTimeCSPDirectives [ directive ] ! . filter (
201+ ( s : string ) => s !== source
202+ )
149203 }
150204}
0 commit comments