Skip to content

Commit a0277f4

Browse files
committed
fix(landing): exclude non-landing routes from the landing CSP fallback
Greptile's second pass caught that proxy.ts's catch-all branch (which serves generateLandingRuntimeCSP()) also handles several non-landing pages that fall through the earlier explicit branches: /verify, /sso, /reset-password (auth sub-pages), /resume/[workflowId] (interfaces), /f/[token] (file shares), /playground, and the authenticated/callbackUrl invite fallthrough. None of these render the HubSpot loader, so they shouldn't get its CSP allowance either. Add an explicit non-landing path prefix list and only fall back to generateRuntimeCSP() (the tight policy) for those, keeping generateLandingRuntimeCSP() for everything else in the catch-all.
1 parent c84d9fc commit a0277f4

1 file changed

Lines changed: 30 additions & 3 deletions

File tree

apps/sim/proxy.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,29 @@ function buildPreflightResponse(policy: CorsPolicy): NextResponse {
117117
return response
118118
}
119119

120+
/**
121+
* Top-level pages outside the (landing) route group that still reach the
122+
* catch-all branch below (auth sub-pages, invite links, file shares, the
123+
* playground). These get the tight app CSP rather than the landing-only
124+
* HubSpot allowance. Keep in sync with apps/sim/app/(auth)/**,
125+
* apps/sim/app/(interfaces)/**, and the other non-landing top-level routes.
126+
*/
127+
const NON_LANDING_PATH_PREFIXES = [
128+
'/verify',
129+
'/sso',
130+
'/reset-password',
131+
'/resume',
132+
'/f',
133+
'/invite',
134+
'/playground',
135+
]
136+
137+
function isNonLandingPath(pathname: string): boolean {
138+
return NON_LANDING_PATH_PREFIXES.some(
139+
(prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`)
140+
)
141+
}
142+
120143
const SUSPICIOUS_UA_PATTERNS = [
121144
/^\s*$/, // Empty user agents
122145
/\.\./, // Path traversal attempt
@@ -284,12 +307,16 @@ export async function proxy(request: NextRequest) {
284307
const securityBlock = handleSecurityFiltering(request)
285308
if (securityBlock) return track(request, securityBlock)
286309

287-
// Everything else is the public marketing/landing site — the only place
288-
// the HubSpot tracking loader renders — so it gets the landing-scoped CSP.
310+
// Everything else falling through here is the public marketing/landing
311+
// site — except a handful of non-landing pages (auth sub-pages, invite
312+
// links, file shares, the playground) that reach this branch too. Only
313+
// the landing site renders the HubSpot loader, so only it gets the
314+
// landing-scoped CSP.
289315
const response = NextResponse.next()
290316
response.headers.set('Vary', 'User-Agent')
291317

292-
response.headers.set('Content-Security-Policy', generateLandingRuntimeCSP())
318+
const csp = isNonLandingPath(url.pathname) ? generateRuntimeCSP() : generateLandingRuntimeCSP()
319+
response.headers.set('Content-Security-Policy', csp)
293320
response.headers.set('X-Content-Type-Options', 'nosniff')
294321
response.headers.set('X-Frame-Options', 'SAMEORIGIN')
295322

0 commit comments

Comments
 (0)