Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/sim/lib/core/security/csp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ vi.mock('@/lib/core/config/env', () =>

vi.mock('@/lib/core/config/feature-flags', () => ({
isDev: false,
isHosted: false,
isReactGrabEnabled: false,
}))

Expand Down
28 changes: 23 additions & 5 deletions apps/sim/lib/core/security/csp.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { env, getEnv } from '../config/env'
import { isDev, isReactGrabEnabled } from '../config/feature-flags'
import { isDev, isHosted, isReactGrabEnabled } from '../config/feature-flags'

/**
* Content Security Policy (CSP) configuration builder
Expand Down Expand Up @@ -42,6 +42,7 @@ export const buildTimeCSPDirectives: CSPDirectives = {
'https://assets.onedollarstats.com',
'https://challenges.cloudflare.com',
...(isReactGrabEnabled ? ['https://unpkg.com'] : []),
...(isHosted ? ['https://www.googletagmanager.com', 'https://www.google-analytics.com'] : []),
],

'style-src': ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com'],
Expand All @@ -59,6 +60,7 @@ export const buildTimeCSPDirectives: CSPDirectives = {
'https://s3.amazonaws.com',
'https://github.com/*',
'https://collector.onedollarstats.com',
...(isHosted ? ['https://www.googletagmanager.com', 'https://www.google-analytics.com'] : []),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSP img-src uses exact domains instead of wildcards

Medium Severity

The img-src directive uses exact www. domains (https://www.google-analytics.com, https://www.googletagmanager.com) while the connect-src directive in the same PR correctly uses wildcards (https://*.google-analytics.com). Google's official CSP documentation for GA4 explicitly recommends wildcards for img-src too: https://*.google-analytics.com https://*.googletagmanager.com. GA tracking pixels can be served from non-www subdomains (e.g., regional endpoints), which would be blocked by the exact-domain img-src entries. This inconsistency exists in both buildTimeCSPDirectives and gtmImg in generateRuntimeCSP().

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1ce2a2f. Configure here.

...(env.S3_BUCKET_NAME && env.AWS_REGION
? [`https://${env.S3_BUCKET_NAME}.s3.${env.AWS_REGION}.amazonaws.com`]
: []),
Expand Down Expand Up @@ -105,6 +107,13 @@ export const buildTimeCSPDirectives: CSPDirectives = {
'https://github.com/*',
'https://challenges.cloudflare.com',
'https://collector.onedollarstats.com',
...(isHosted
? [
'https://www.googletagmanager.com',
'https://*.google-analytics.com',
'https://*.analytics.google.com',
]
: []),
...getHostnameFromUrl(env.NEXT_PUBLIC_BRAND_LOGO_URL),
...getHostnameFromUrl(env.NEXT_PUBLIC_PRIVACY_URL),
...getHostnameFromUrl(env.NEXT_PUBLIC_TERMS_URL),
Expand All @@ -116,6 +125,7 @@ export const buildTimeCSPDirectives: CSPDirectives = {
'https://drive.google.com',
'https://docs.google.com',
'https://*.google.com',
...(isHosted ? ['https://www.googletagmanager.com'] : []),
],

'frame-ancestors': ["'self'"],
Expand Down Expand Up @@ -171,16 +181,24 @@ export function generateRuntimeCSP(): string {
const brandLogoDomain = brandLogoDomains[0] || ''
const brandFaviconDomain = brandFaviconDomains[0] || ''
const reactGrabScript = isReactGrabEnabled ? 'https://unpkg.com' : ''
const gtmScript = isHosted
? 'https://www.googletagmanager.com https://www.google-analytics.com'
: ''
const gtmConnect = isHosted
? 'https://www.googletagmanager.com https://*.google-analytics.com https://*.analytics.google.com'
: ''
const gtmImg = isHosted ? 'https://www.googletagmanager.com https://www.google-analytics.com' : ''
const gtmFrame = isHosted ? 'https://www.googletagmanager.com' : ''

return `
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.google.com https://apis.google.com https://assets.onedollarstats.com https://challenges.cloudflare.com ${reactGrabScript};
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.google.com https://apis.google.com https://assets.onedollarstats.com https://challenges.cloudflare.com ${reactGrabScript} ${gtmScript};
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 https://*.s3.amazonaws.com https://s3.amazonaws.com https://*.amazonaws.com https://*.blob.core.windows.net https://github.com/* https://collector.onedollarstats.com ${brandLogoDomain} ${brandFaviconDomain};
img-src 'self' data: blob: https://*.googleusercontent.com https://*.google.com https://*.atlassian.com https://cdn.discordapp.com https://*.githubusercontent.com https://*.s3.amazonaws.com https://s3.amazonaws.com https://*.amazonaws.com https://*.blob.core.windows.net https://github.com/* https://collector.onedollarstats.com ${gtmImg} ${brandLogoDomain} ${brandFaviconDomain};
media-src 'self' blob:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' ${appUrl} ${ollamaUrl} ${socketUrl} ${socketWsUrl} 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://api.github.com https://github.com/* https://*.atlassian.com https://*.supabase.co https://challenges.cloudflare.com https://collector.onedollarstats.com ${dynamicDomainsStr};
frame-src 'self' https://challenges.cloudflare.com https://drive.google.com https://docs.google.com https://*.google.com;
connect-src 'self' ${appUrl} ${ollamaUrl} ${socketUrl} ${socketWsUrl} 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://api.github.com https://github.com/* https://*.atlassian.com https://*.supabase.co https://challenges.cloudflare.com https://collector.onedollarstats.com ${gtmConnect} ${dynamicDomainsStr};
frame-src 'self' https://challenges.cloudflare.com https://drive.google.com https://docs.google.com https://*.google.com ${gtmFrame};
frame-ancestors 'self';
form-action 'self';
base-uri 'self';
Expand Down
Loading