Skip to content

Commit e392ca4

Browse files
authored
fix(csp): created csp utils file that holds the csp for organization (#752)
1 parent a217dac commit e392ca4

File tree

2 files changed

+150
-2
lines changed

2 files changed

+150
-2
lines changed

apps/sim/lib/security/csp.ts

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { env } from '../env'
2+
3+
/**
4+
* Content Security Policy (CSP) configuration builder
5+
* This creates a more maintainable and readable CSP configuration
6+
*/
7+
8+
export interface CSPDirectives {
9+
'default-src'?: string[]
10+
'script-src'?: string[]
11+
'style-src'?: string[]
12+
'img-src'?: string[]
13+
'media-src'?: string[]
14+
'font-src'?: string[]
15+
'connect-src'?: string[]
16+
'frame-src'?: string[]
17+
'frame-ancestors'?: string[]
18+
'form-action'?: string[]
19+
'base-uri'?: string[]
20+
'object-src'?: string[]
21+
}
22+
23+
export const cspDirectives: CSPDirectives = {
24+
'default-src': ["'self'"],
25+
26+
'script-src': [
27+
"'self'",
28+
"'unsafe-inline'",
29+
"'unsafe-eval'",
30+
'https://*.google.com',
31+
'https://apis.google.com',
32+
'https://*.vercel-scripts.com',
33+
'https://*.vercel-insights.com',
34+
'https://vercel.live',
35+
'https://*.vercel.live',
36+
'https://vercel.com',
37+
'https://*.vercel.app',
38+
'https://vitals.vercel-insights.com',
39+
],
40+
41+
'style-src': ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com'],
42+
43+
'img-src': [
44+
"'self'",
45+
'data:',
46+
'blob:',
47+
'https://*.googleusercontent.com',
48+
'https://*.google.com',
49+
'https://*.atlassian.com',
50+
'https://cdn.discordapp.com',
51+
'https://*.githubusercontent.com',
52+
],
53+
54+
'media-src': ["'self'", 'blob:'],
55+
56+
'font-src': ["'self'", 'https://fonts.gstatic.com'],
57+
58+
'connect-src': [
59+
"'self'",
60+
env.NEXT_PUBLIC_APP_URL || '',
61+
env.OLLAMA_URL || 'http://localhost:11434',
62+
env.NEXT_PUBLIC_SOCKET_URL || 'http://localhost:3002',
63+
env.NEXT_PUBLIC_SOCKET_URL?.replace('http://', 'ws://').replace('https://', 'wss://') ||
64+
'ws://localhost:3002',
65+
'https://*.up.railway.app',
66+
'wss://*.up.railway.app',
67+
'https://api.browser-use.com',
68+
'https://api.exa.ai',
69+
'https://api.firecrawl.dev',
70+
'https://*.googleapis.com',
71+
'https://*.amazonaws.com',
72+
'https://*.s3.amazonaws.com',
73+
'https://*.blob.core.windows.net',
74+
'https://*.vercel-insights.com',
75+
'https://vitals.vercel-insights.com',
76+
'https://*.atlassian.com',
77+
'https://*.supabase.co',
78+
'https://vercel.live',
79+
'https://*.vercel.live',
80+
'https://vercel.com',
81+
'https://*.vercel.app',
82+
'wss://*.vercel.app',
83+
],
84+
85+
// Google Picker and Drive integration
86+
'frame-src': [
87+
'https://drive.google.com',
88+
'https://docs.google.com', // Required for Google Picker
89+
'https://*.google.com',
90+
],
91+
92+
'frame-ancestors': ["'self'"],
93+
'form-action': ["'self'"],
94+
'base-uri': ["'self'"],
95+
'object-src': ["'none'"],
96+
}
97+
98+
/**
99+
* Build CSP string from directives object
100+
*/
101+
export function buildCSPString(directives: CSPDirectives): string {
102+
return Object.entries(directives)
103+
.map(([directive, sources]) => {
104+
if (!sources || sources.length === 0) return ''
105+
// Filter out empty strings
106+
const validSources = sources.filter((source: string) => source && source.trim() !== '')
107+
if (validSources.length === 0) return ''
108+
return `${directive} ${validSources.join(' ')}`
109+
})
110+
.filter(Boolean)
111+
.join('; ')
112+
}
113+
114+
/**
115+
* Get the main CSP policy string
116+
*/
117+
export function getMainCSPPolicy(): string {
118+
return buildCSPString(cspDirectives)
119+
}
120+
121+
/**
122+
* Permissive CSP for workflow execution endpoints
123+
*/
124+
export function getWorkflowExecutionCSPPolicy(): string {
125+
return "default-src * 'unsafe-inline' 'unsafe-eval'; connect-src *;"
126+
}
127+
128+
/**
129+
* Add a source to a specific directive
130+
*/
131+
export function addCSPSource(directive: keyof CSPDirectives, source: string): void {
132+
if (!cspDirectives[directive]) {
133+
cspDirectives[directive] = []
134+
}
135+
if (!cspDirectives[directive]!.includes(source)) {
136+
cspDirectives[directive]!.push(source)
137+
}
138+
}
139+
140+
/**
141+
* Remove a source from a specific directive
142+
*/
143+
export function removeCSPSource(directive: keyof CSPDirectives, source: string): void {
144+
if (cspDirectives[directive]) {
145+
cspDirectives[directive] = cspDirectives[directive]!.filter((s: string) => s !== source)
146+
}
147+
}

apps/sim/next.config.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { withSentryConfig } from '@sentry/nextjs'
33
import type { NextConfig } from 'next'
44
import { env, isTruthy } from './lib/env'
55
import { isDev, isProd } from './lib/environment'
6+
import { getMainCSPPolicy, getWorkflowExecutionCSPPolicy } from './lib/security/csp'
67

78
const nextConfig: NextConfig = {
89
devIndicators: false,
@@ -90,7 +91,7 @@ const nextConfig: NextConfig = {
9091
{ key: 'Cross-Origin-Opener-Policy', value: 'unsafe-none' },
9192
{
9293
key: 'Content-Security-Policy',
93-
value: "default-src * 'unsafe-inline' 'unsafe-eval'; connect-src *;",
94+
value: getWorkflowExecutionCSPPolicy(),
9495
},
9596
],
9697
},
@@ -146,7 +147,7 @@ const nextConfig: NextConfig = {
146147
},
147148
{
148149
key: 'Content-Security-Policy',
149-
value: `default-src 'self'; 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; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: blob: https://*.googleusercontent.com https://*.google.com https://*.atlassian.com https://cdn.discordapp.com https://*.githubusercontent.com; media-src 'self' blob:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' ${env.NEXT_PUBLIC_APP_URL || ''} ${env.OLLAMA_URL || 'http://localhost:11434'} ${env.NEXT_PUBLIC_SOCKET_URL || 'http://localhost:3002'} ${env.NEXT_PUBLIC_SOCKET_URL?.replace('http://', 'ws://').replace('https://', 'wss://') || 'ws://localhost:3002'} 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; frame-src https://drive.google.com https://*.google.com; frame-ancestors 'self'; form-action 'self'; base-uri 'self'; object-src 'none'`,
150+
value: getMainCSPPolicy(),
150151
},
151152
],
152153
},

0 commit comments

Comments
 (0)