@@ -2,9 +2,17 @@ import { env, getEnv } from '../env'
22
33/**
44 * Content Security Policy (CSP) configuration builder
5- * This creates a more maintainable and readable CSP configuration
65 */
76
7+ function getHostnameFromUrl ( url : string | undefined ) : string [ ] {
8+ if ( ! url ) return [ ]
9+ try {
10+ return [ `https://${ new URL ( url ) . hostname } ` ]
11+ } catch {
12+ return [ ]
13+ }
14+ }
15+
816export interface CSPDirectives {
917 'default-src' ?: string [ ]
1018 'script-src' ?: string [ ]
@@ -43,7 +51,6 @@ export const buildTimeCSPDirectives: CSPDirectives = {
4351 'style-src' : [ "'self'" , "'unsafe-inline'" , 'https://fonts.googleapis.com' ] ,
4452
4553 'img-src' : [
46- 'https://agentics.epiqglobal.com' ,
4754 "'self'" ,
4855 'data:' ,
4956 'blob:' ,
@@ -66,6 +73,7 @@ export const buildTimeCSPDirectives: CSPDirectives = {
6673 : [ ] ) ,
6774 'https://*.amazonaws.com' ,
6875 'https://*.blob.core.windows.net' ,
76+ ...getHostnameFromUrl ( env . NEXT_PUBLIC_BRAND_LOGO_URL ) ,
6977 ] ,
7078
7179 'media-src' : [ "'self'" , 'blob:' ] ,
@@ -98,14 +106,12 @@ export const buildTimeCSPDirectives: CSPDirectives = {
98106 'https://*.vercel.app' ,
99107 'wss://*.vercel.app' ,
100108 'https://pro.ip-api.com' ,
109+ ...getHostnameFromUrl ( env . NEXT_PUBLIC_BRAND_LOGO_URL ) ,
110+ ...getHostnameFromUrl ( env . NEXT_PUBLIC_PRIVACY_URL ) ,
111+ ...getHostnameFromUrl ( env . NEXT_PUBLIC_TERMS_URL ) ,
101112 ] ,
102113
103- // Google Picker and Drive integration
104- 'frame-src' : [
105- 'https://drive.google.com' ,
106- 'https://docs.google.com' , // Required for Google Picker
107- 'https://*.google.com' ,
108- ] ,
114+ 'frame-src' : [ 'https://drive.google.com' , 'https://docs.google.com' , 'https://*.google.com' ] ,
109115
110116 'frame-ancestors' : [ "'self'" ] ,
111117 'form-action' : [ "'self'" ] ,
@@ -120,7 +126,6 @@ export function buildCSPString(directives: CSPDirectives): string {
120126 return Object . entries ( directives )
121127 . map ( ( [ directive , sources ] ) => {
122128 if ( ! sources || sources . length === 0 ) return ''
123- // Filter out empty strings
124129 const validSources = sources . filter ( ( source : string ) => source && source . trim ( ) !== '' )
125130 if ( validSources . length === 0 ) return ''
126131 return `${ directive } ${ validSources . join ( ' ' ) } `
@@ -140,14 +145,23 @@ export function generateRuntimeCSP(): string {
140145 const appUrl = getEnv ( 'NEXT_PUBLIC_APP_URL' ) || ''
141146 const ollamaUrl = getEnv ( 'OLLAMA_URL' ) || 'http://localhost:11434'
142147
148+ const brandLogoDomains = getHostnameFromUrl ( getEnv ( 'NEXT_PUBLIC_BRAND_LOGO_URL' ) )
149+ const privacyDomains = getHostnameFromUrl ( getEnv ( 'NEXT_PUBLIC_PRIVACY_URL' ) )
150+ const termsDomains = getHostnameFromUrl ( getEnv ( 'NEXT_PUBLIC_TERMS_URL' ) )
151+
152+ const allDynamicDomains = [ ...brandLogoDomains , ...privacyDomains , ...termsDomains ]
153+ const uniqueDynamicDomains = Array . from ( new Set ( allDynamicDomains ) )
154+ const dynamicDomainsStr = uniqueDynamicDomains . join ( ' ' )
155+ const brandLogoDomain = brandLogoDomains [ 0 ] || ''
156+
143157 return `
144158 default-src 'self';
145159 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;
146160 style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
147- 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;
161+ 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 ${ brandLogoDomain } ;
148162 media-src 'self' blob:;
149163 font-src 'self' https://fonts.gstatic.com;
150- 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;
164+ 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 ${ dynamicDomainsStr } ;
151165 frame-src https://drive.google.com https://docs.google.com https://*.google.com;
152166 frame-ancestors 'self';
153167 form-action 'self';
0 commit comments