diff --git a/apps/sim/app/(landing)/privacy/page.tsx b/apps/sim/app/(landing)/privacy/page.tsx index 43dfb5a96c..b4bb400fc0 100644 --- a/apps/sim/app/(landing)/privacy/page.tsx +++ b/apps/sim/app/(landing)/privacy/page.tsx @@ -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 (
diff --git a/apps/sim/app/(landing)/terms/page.tsx b/apps/sim/app/(landing)/terms/page.tsx index d24b6e3ef0..24c3c4374d 100644 --- a/apps/sim/app/(landing)/terms/page.tsx +++ b/apps/sim/app/(landing)/terms/page.tsx @@ -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 (
diff --git a/apps/sim/lib/security/csp.ts b/apps/sim/lib/security/csp.ts index be5002cf73..42d14e9ce1 100644 --- a/apps/sim/lib/security/csp.ts +++ b/apps/sim/lib/security/csp.ts @@ -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:'], @@ -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}; diff --git a/apps/sim/middleware.ts b/apps/sim/middleware.ts index de4a800044..a211e09121 100644 --- a/apps/sim/middleware.ts +++ b/apps/sim/middleware.ts @@ -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) { diff --git a/apps/sim/next.config.ts b/apps/sim/next.config.ts index d7224c5585..3da0ea256d 100644 --- a/apps/sim/next.config.ts +++ b/apps/sim/next.config.ts @@ -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: {