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
8 changes: 8 additions & 0 deletions apps/sim/app/(landing)/privacy/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
'use client'

import { useEffect } from 'react'
import Link from 'next/link'
import { getEnv } from '@/lib/env'
import { LegalLayout } from '@/app/(landing)/components'

export default function PrivacyPolicy() {
useEffect(() => {
const privacyUrl = getEnv('NEXT_PUBLIC_PRIVACY_URL')
if (privacyUrl?.startsWith('http')) {
window.location.href = privacyUrl
}
}, [])
return (
<LegalLayout title='Privacy Policy'>
<section>
Expand Down
8 changes: 8 additions & 0 deletions apps/sim/app/(landing)/terms/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
'use client'

import { useEffect } from 'react'
import Link from 'next/link'
import { getEnv } from '@/lib/env'
import { LegalLayout } from '@/app/(landing)/components'

export default function TermsOfService() {
useEffect(() => {
const termsUrl = getEnv('NEXT_PUBLIC_TERMS_URL')
if (termsUrl?.startsWith('http')) {
window.location.href = termsUrl
}
}, [])
return (
<LegalLayout title='Terms of Service'>
<section>
Expand Down
12 changes: 10 additions & 2 deletions apps/sim/lib/security/csp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export const buildTimeCSPDirectives: CSPDirectives = {
'https://*.amazonaws.com',
'https://*.blob.core.windows.net',
...getHostnameFromUrl(env.NEXT_PUBLIC_BRAND_LOGO_URL),
...getHostnameFromUrl(env.NEXT_PUBLIC_BRAND_FAVICON_URL),
],

'media-src': ["'self'", 'blob:'],
Expand Down Expand Up @@ -146,19 +147,26 @@ export function generateRuntimeCSP(): string {
const ollamaUrl = getEnv('OLLAMA_URL') || 'http://localhost:11434'

const brandLogoDomains = getHostnameFromUrl(getEnv('NEXT_PUBLIC_BRAND_LOGO_URL'))
const brandFaviconDomains = getHostnameFromUrl(getEnv('NEXT_PUBLIC_BRAND_FAVICON_URL'))
const privacyDomains = getHostnameFromUrl(getEnv('NEXT_PUBLIC_PRIVACY_URL'))
const termsDomains = getHostnameFromUrl(getEnv('NEXT_PUBLIC_TERMS_URL'))

const allDynamicDomains = [...brandLogoDomains, ...privacyDomains, ...termsDomains]
const allDynamicDomains = [
...brandLogoDomains,
...brandFaviconDomains,
...privacyDomains,
...termsDomains,
]
const uniqueDynamicDomains = Array.from(new Set(allDynamicDomains))
const dynamicDomainsStr = uniqueDynamicDomains.join(' ')
const brandLogoDomain = brandLogoDomains[0] || ''
const brandFaviconDomain = brandFaviconDomains[0] || ''

return `
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 https://b2bjsstore.s3.us-west-2.amazonaws.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 https://*.public.blob.vercel-storage.com ${brandLogoDomain};
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} ${brandFaviconDomain};
media-src 'self' blob:;
font-src 'self' https://fonts.gstatic.com;
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};
Expand Down
31 changes: 0 additions & 31 deletions apps/sim/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,37 +93,6 @@ export async function middleware(request: NextRequest) {
}
}

// Handle whitelabel redirects for terms and privacy pages
if (url.pathname === '/terms') {
const termsUrl = process.env.NEXT_PUBLIC_TERMS_URL
if (termsUrl?.startsWith('http')) {
return NextResponse.redirect(termsUrl)
}
}

if (url.pathname === '/privacy') {
const privacyUrl = process.env.NEXT_PUBLIC_PRIVACY_URL
if (privacyUrl?.startsWith('http')) {
return NextResponse.redirect(privacyUrl)
}
}

// Legacy redirect: /w -> /workspace (will be handled by workspace layout)
if (url.pathname === '/w' || url.pathname.startsWith('/w/')) {
// Extract workflow ID if present
const pathParts = url.pathname.split('/')
if (pathParts.length >= 3 && pathParts[1] === 'w') {
const workflowId = pathParts[2]
// Redirect old workflow URLs to new format
// We'll need to resolve the workspace ID for this workflow
return NextResponse.redirect(
new URL(`/workspace?redirect_workflow=${workflowId}`, request.url)
)
}
// Simple /w redirect to workspace root
return NextResponse.redirect(new URL('/workspace', request.url))
}

// Handle login page - redirect authenticated users to workspace
if (url.pathname === '/login' || url.pathname === '/signup') {
if (hasActiveSession) {
Expand Down
30 changes: 30 additions & 0 deletions apps/sim/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,36 @@ const nextConfig: NextConfig = {
},
]
: []),
// Brand logo domain if configured
...(env.NEXT_PUBLIC_BRAND_LOGO_URL
? (() => {
try {
return [
{
protocol: 'https' as const,
hostname: new URL(env.NEXT_PUBLIC_BRAND_LOGO_URL).hostname,
},
]
} catch {
return []
}
})()
: []),
// Brand favicon domain if configured
...(env.NEXT_PUBLIC_BRAND_FAVICON_URL
? (() => {
try {
return [
{
protocol: 'https' as const,
hostname: new URL(env.NEXT_PUBLIC_BRAND_FAVICON_URL).hostname,
},
]
} catch {
return []
}
})()
: []),
],
},
typescript: {
Expand Down