Skip to content
Open
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
10 changes: 1 addition & 9 deletions apps/sim/app/api/files/delete/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ vi.mock('fs/promises', () => ({
}))

import { createMockRequest } from '@sim/testing'
import { OPTIONS, POST } from '@/app/api/files/delete/route'
import { POST } from '@/app/api/files/delete/route'

describe('File Delete API Route', () => {
beforeEach(() => {
Expand Down Expand Up @@ -198,12 +198,4 @@ describe('File Delete API Route', () => {
expect(data).toHaveProperty('error', 'InvalidRequestError')
expect(data).toHaveProperty('message', 'No file path provided')
})

it('should handle CORS preflight requests', async () => {
const response = await OPTIONS()

expect(response.status).toBe(204)
expect(response.headers.get('Access-Control-Allow-Methods')).toBe('GET, POST, DELETE, OPTIONS')
expect(response.headers.get('Access-Control-Allow-Headers')).toBe('Content-Type')
})
})
8 changes: 0 additions & 8 deletions apps/sim/app/api/files/delete/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { extractStorageKey, inferContextFromKey } from '@/lib/uploads/utils/file
import { verifyFileAccess } from '@/app/api/files/authorization'
import {
createErrorResponse,
createOptionsResponse,
createSuccessResponse,
extractFilename,
FileNotFoundError,
Expand Down Expand Up @@ -119,10 +118,3 @@ function extractStorageKeyFromPath(filePath: string): string {

return extractFilename(filePath)
}

/**
* Handle CORS preflight requests
*/
export const OPTIONS = withRouteHandler(async () => {
return createOptionsResponse()
})
14 changes: 0 additions & 14 deletions apps/sim/app/api/files/presigned/batch/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,17 +156,3 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
)
}
})

export const OPTIONS = withRouteHandler(async () => {
return NextResponse.json(
{},
{
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
)
})
14 changes: 1 addition & 13 deletions apps/sim/app/api/files/presigned/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ vi.mock('@/lib/uploads', () => ({
isUsingCloudStorage: mockIsUsingCloudStorageUploads,
}))

import { OPTIONS, POST } from '@/app/api/files/presigned/route'
import { POST } from '@/app/api/files/presigned/route'

const defaultMockUser = {
id: 'test-user-id',
Expand Down Expand Up @@ -827,16 +827,4 @@ describe('/api/files/presigned', () => {
expect(mockValidateAttachmentFileType).not.toHaveBeenCalled()
})
})

describe('OPTIONS', () => {
it('should handle CORS preflight requests', async () => {
const response = await OPTIONS()

expect(response.status).toBe(200)
expect(response.headers.get('Access-Control-Allow-Methods')).toBe('POST, OPTIONS')
expect(response.headers.get('Access-Control-Allow-Headers')).toBe(
'Content-Type, Authorization'
)
})
})
})
14 changes: 0 additions & 14 deletions apps/sim/app/api/files/presigned/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,17 +310,3 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
)
}
})

export const OPTIONS = withRouteHandler(async () => {
return NextResponse.json(
{},
{
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
)
})
10 changes: 1 addition & 9 deletions apps/sim/app/api/files/upload/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ vi.mock('@/lib/uploads/setup.server', () => ({
}))

import { uploadWorkspaceFile } from '@/lib/uploads/contexts/workspace'
import { OPTIONS, POST } from '@/app/api/files/upload/route'
import { POST } from '@/app/api/files/upload/route'

/**
* Configure mocks for authenticated file upload tests
Expand Down Expand Up @@ -307,14 +307,6 @@ describe('File Upload API Route', () => {
expect(data).toHaveProperty('error')
expect(typeof data.error).toBe('string')
})

it('should handle CORS preflight requests', async () => {
const response = await OPTIONS()

expect(response.status).toBe(204)
expect(response.headers.get('Access-Control-Allow-Methods')).toBe('GET, POST, DELETE, OPTIONS')
expect(response.headers.get('Access-Control-Allow-Headers')).toBe('Content-Type')
})
})

describe('File Upload Security Tests', () => {
Expand Down
10 changes: 1 addition & 9 deletions apps/sim/app/api/files/upload/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@ import {
validateFileType,
} from '@/lib/uploads/utils/validation'
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
import {
createErrorResponse,
createOptionsResponse,
InvalidRequestError,
} from '@/app/api/files/utils'
import { createErrorResponse, InvalidRequestError } from '@/app/api/files/utils'

const ALLOWED_EXTENSIONS = new Set<string>(SUPPORTED_ATTACHMENT_EXTENSIONS)

Expand Down Expand Up @@ -430,7 +426,3 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
return createErrorResponse(error instanceof Error ? error : new Error('File upload failed'))
}
})

export const OPTIONS = withRouteHandler(async () => {
return createOptionsResponse()
})
10 changes: 0 additions & 10 deletions apps/sim/app/api/files/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,13 +238,3 @@ export function createErrorResponse(error: Error, status = 500): NextResponse {
export function createSuccessResponse(data: ApiSuccessResponse): NextResponse {
return NextResponse.json(data)
}

export function createOptionsResponse(): NextResponse {
return new NextResponse(null, {
status: 204,
headers: {
'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
},
})
}
4 changes: 0 additions & 4 deletions apps/sim/app/api/form/[identifier]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,3 @@ export const GET = withRouteHandler(
}
}
)

export const OPTIONS = withRouteHandler(async (request: NextRequest) => {
return addCorsHeaders(new NextResponse(null, { status: 204 }), request)
})
13 changes: 0 additions & 13 deletions apps/sim/app/api/mcp/copilot/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,19 +386,6 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
}
})

export const OPTIONS = withRouteHandler(async () => {
return new NextResponse(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, DELETE',
'Access-Control-Allow-Headers':
'Content-Type, Authorization, X-API-Key, X-Requested-With, Accept',
'Access-Control-Max-Age': '86400',
},
})
})

export const DELETE = withRouteHandler(async (request: NextRequest) => {
void request
return NextResponse.json(createError(0, -32000, 'Method not allowed.'), { status: 405 })
Expand Down
18 changes: 0 additions & 18 deletions apps/sim/app/api/templates/approved/sanitized/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,21 +131,3 @@ export const GET = withRouteHandler(async (request: NextRequest) => {
)
}
})

// Add a helpful OPTIONS handler for CORS preflight
export const OPTIONS = withRouteHandler(async (request: NextRequest) => {
const requestId = generateRequestId()
const queryValidation = noInputSchema.safeParse(
Object.fromEntries(request.nextUrl.searchParams.entries())
)
if (!queryValidation.success) return validationErrorResponse(queryValidation.error)
logger.info(`[${requestId}] OPTIONS request received for /api/templates/approved/sanitized`)

return new NextResponse(null, {
status: 200,
headers: {
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'X-API-Key, Content-Type',
},
})
})
12 changes: 0 additions & 12 deletions apps/sim/app/api/tools/image/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,3 @@ export const GET = withRouteHandler(async (request: NextRequest) => {
})
}
})

export const OPTIONS = withRouteHandler(async () => {
return new NextResponse(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
},
})
})
1 change: 1 addition & 0 deletions apps/sim/lib/core/security/deployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export function addCorsHeaders(response: NextResponse, request: NextRequest): Ne
response.headers.set('Access-Control-Allow-Origin', origin)
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, X-Requested-With')
response.headers.set('Vary', 'Origin')
}

return response
Expand Down
87 changes: 1 addition & 86 deletions apps/sim/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,85 +156,10 @@ const nextConfig: NextConfig = {
{ key: 'Access-Control-Allow-Headers', value: 'Content-Type, Accept' },
],
},
{
// API routes CORS headers
source: '/api/:path*',
headers: [
{ key: 'Access-Control-Allow-Credentials', value: 'true' },
{
key: 'Access-Control-Allow-Origin',
value: env.NEXT_PUBLIC_APP_URL || 'http://localhost:3001',
},
{
key: 'Access-Control-Allow-Methods',
value: 'GET,POST,OPTIONS,PUT,DELETE',
},
{
key: 'Access-Control-Allow-Headers',
value:
'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-API-Key, Authorization',
},
],
},
{
source: '/api/auth/oauth2/:path*',
headers: [
{ key: 'Access-Control-Allow-Credentials', value: 'false' },
{ key: 'Access-Control-Allow-Origin', value: '*' },
{ key: 'Access-Control-Allow-Methods', value: 'GET, POST, OPTIONS' },
{
key: 'Access-Control-Allow-Headers',
value: 'Content-Type, Authorization, Accept',
},
],
},
{
source: '/api/auth/jwks',
headers: [
{ key: 'Access-Control-Allow-Credentials', value: 'false' },
{ key: 'Access-Control-Allow-Origin', value: '*' },
{ key: 'Access-Control-Allow-Methods', value: 'GET, OPTIONS' },
{ key: 'Access-Control-Allow-Headers', value: 'Content-Type, Accept' },
],
},
{
source: '/api/auth/.well-known/:path*',
headers: [
{ key: 'Access-Control-Allow-Credentials', value: 'false' },
{ key: 'Access-Control-Allow-Origin', value: '*' },
{ key: 'Access-Control-Allow-Methods', value: 'GET, OPTIONS' },
{ key: 'Access-Control-Allow-Headers', value: 'Content-Type, Accept' },
],
},
{
source: '/api/mcp/copilot',
headers: [
{ key: 'Access-Control-Allow-Credentials', value: 'false' },
{ key: 'Access-Control-Allow-Origin', value: '*' },
{
key: 'Access-Control-Allow-Methods',
value: 'GET, POST, OPTIONS, DELETE',
},
{
key: 'Access-Control-Allow-Headers',
value: 'Content-Type, Authorization, X-API-Key, X-Requested-With, Accept',
},
],
},
// For workflow execution API endpoints
// /api/* CORS is set at runtime in proxy.ts (resolveApiCorsPolicy).
{
source: '/api/workflows/:id/execute',
headers: [
{ key: 'Access-Control-Allow-Origin', value: '*' },
{
key: 'Access-Control-Allow-Methods',
value: 'GET,POST,OPTIONS,PUT',
},
{
key: 'Access-Control-Allow-Headers',
value:
'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-API-Key',
},
{ key: 'Cross-Origin-Embedder-Policy', value: 'unsafe-none' },
{ key: 'Cross-Origin-Opener-Policy', value: 'unsafe-none' },
{
Expand Down Expand Up @@ -317,16 +242,6 @@ const nextConfig: NextConfig = {
{ key: 'Cross-Origin-Opener-Policy', value: 'unsafe-none' },
],
},
// Form API routes - allow cross-origin requests
{
source: '/api/form/:path*',
headers: [
{ key: 'Access-Control-Allow-Origin', value: '*' },
{ key: 'Access-Control-Allow-Methods', value: 'GET, POST, OPTIONS' },
{ key: 'Access-Control-Allow-Headers', value: 'Content-Type, X-Requested-With' },
{ key: 'Access-Control-Allow-Credentials', value: 'true' },
],
},
// Apply security headers to routes not handled by middleware runtime CSP
// Middleware handles: /, /login, /signup, /workspace/*
// Exclude chat and form routes which have their own permissive embed headers
Expand Down
Loading
Loading