Skip to content

Commit 4e61bb0

Browse files
committed
fix(security): add Vary: Origin and avoid double CORS on form routes
- Set Vary: Origin whenever CORS Allow-Origin is not '*' (proxy + addCorsHeaders) to prevent shared caches from serving the wrong reflected origin - Skip middleware CORS for /api/form/* non-OPTIONS; route handler's addCorsHeaders owns the reflected-origin response to avoid the same header being written twice - Comment why /api/* short-circuits before handleSecurityFiltering
1 parent 3a8349d commit 4e61bb0

2 files changed

Lines changed: 12 additions & 0 deletions

File tree

apps/sim/lib/core/security/deployment.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export function addCorsHeaders(response: NextResponse, request: NextRequest): Ne
106106
response.headers.set('Access-Control-Allow-Origin', origin)
107107
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
108108
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, X-Requested-With')
109+
response.headers.set('Vary', 'Origin')
109110
}
110111

111112
return response

apps/sim/proxy.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ function applyCorsHeaders(response: NextResponse, policy: CorsPolicy): void {
8585
response.headers.set('Access-Control-Allow-Credentials', String(policy.credentials))
8686
response.headers.set('Access-Control-Allow-Methods', policy.methods)
8787
response.headers.set('Access-Control-Allow-Headers', policy.headers)
88+
if (policy.origin !== '*') {
89+
response.headers.set('Vary', 'Origin')
90+
}
8891
}
8992

9093
/**
@@ -212,11 +215,19 @@ function handleSecurityFiltering(request: NextRequest): NextResponse | null {
212215
export async function proxy(request: NextRequest) {
213216
const url = request.nextUrl
214217

218+
// /api/* short-circuits before handleSecurityFiltering — UA filtering was
219+
// never applied to API routes under the prior matcher, and webhook/MCP
220+
// exemptions in handleSecurityFiltering exist for non-API paths under /.
215221
if (url.pathname.startsWith('/api/')) {
216222
const policy = resolveApiCorsPolicy(request)
217223
if (request.method === 'OPTIONS') {
218224
return buildPreflightResponse(policy)
219225
}
226+
// Form routes reflect origin via addCorsHeaders in the route handler;
227+
// skip middleware CORS here so the same header isn't written twice.
228+
if (url.pathname === '/api/form' || url.pathname.startsWith('/api/form/')) {
229+
return NextResponse.next()
230+
}
220231
const response = NextResponse.next()
221232
applyCorsHeaders(response, policy)
222233
return response

0 commit comments

Comments
 (0)