-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Expand file tree
/
Copy pathproxy.test.ts
More file actions
151 lines (135 loc) · 5.5 KB
/
Copy pathproxy.test.ts
File metadata and controls
151 lines (135 loc) · 5.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/**
* @vitest-environment node
*/
import { createEnvMock } from '@sim/testing'
import type { NextRequest } from 'next/server'
import { describe, expect, it, vi } from 'vitest'
vi.mock('@/lib/core/config/env', () =>
createEnvMock({ NEXT_PUBLIC_APP_URL: 'https://app.sim.test' })
)
import { resolveApiCorsPolicy } from '@/proxy'
function makeRequest(pathname: string, origin?: string): NextRequest {
return {
nextUrl: { pathname },
headers: {
get: (name: string) => (name.toLowerCase() === 'origin' ? (origin ?? null) : null),
},
} as unknown as NextRequest
}
describe('resolveApiCorsPolicy', () => {
it('serves OAuth2 routes with wildcard origin and no credentials', () => {
expect(resolveApiCorsPolicy(makeRequest('/api/auth/oauth2/token'))).toEqual({
origin: '*',
credentials: false,
methods: 'GET, POST, OPTIONS',
headers: 'Content-Type, Authorization, Accept',
})
})
it('serves MCP copilot with DELETE in allowed methods', () => {
const policy = resolveApiCorsPolicy(makeRequest('/api/mcp/copilot'))
expect(policy.origin).toBe('*')
expect(policy.methods).toContain('DELETE')
expect(policy.headers).toContain('X-API-Key')
})
it('reflects origin for chat embeds with credentials enabled', () => {
const paths = ['/api/chat/abc', '/api/chat/abc/otp', '/api/chat/abc/sso']
for (const path of paths) {
const policy = resolveApiCorsPolicy(makeRequest(path, 'https://customer.example'))
expect(policy).toEqual({
origin: 'https://customer.example',
credentials: true,
methods: 'GET, POST, PUT, OPTIONS',
headers: 'Content-Type, X-Requested-With',
})
}
})
it('drops credentials on embed policy when Origin header is absent (CORS spec invariant)', () => {
const policy = resolveApiCorsPolicy(makeRequest('/api/chat/abc'))
expect(policy.origin).toBe('*')
expect(policy.credentials).toBe(false)
})
it('allows PUT on the embed policy (used by OTP verification on /[identifier]/otp)', () => {
const policy = resolveApiCorsPolicy(
makeRequest('/api/chat/abc/otp', 'https://customer.example')
)
expect(policy.methods).toContain('PUT')
})
it('applies the embed policy to future identifier subroutes (not just /otp, /sso)', () => {
const policy = resolveApiCorsPolicy(
makeRequest('/api/chat/abc/transcript', 'https://customer.example')
)
expect(policy.origin).toBe('https://customer.example')
expect(policy.credentials).toBe(true)
})
it('uses the default credentialed policy for workspace-internal chat routes', () => {
const paths = ['/api/chat', '/api/chat/manage/abc', '/api/chat/validate']
for (const path of paths) {
const policy = resolveApiCorsPolicy(makeRequest(path, 'https://customer.example'))
expect(policy.origin).toBe('https://app.sim.test')
expect(policy.credentials).toBe(true)
}
})
it('serves workflow execute with wildcard origin and execution identity header', () => {
const policy = resolveApiCorsPolicy(
makeRequest('/api/workflows/workflow-123/execute', 'https://other.example')
)
expect(policy.origin).toBe('*')
expect(policy.credentials).toBe(false)
expect(policy.methods).toContain('PUT')
expect(policy.headers).toContain('X-Execution-Id')
expect(policy.headers).toContain('X-Execution-Timeout-Seconds')
})
it('serves v2 workflow execute with wildcard origin and the stream-protocol header', () => {
const policy = resolveApiCorsPolicy(
makeRequest('/api/v2/workflows/workflow-123/execute', 'https://other.example')
)
expect(policy.origin).toBe('*')
expect(policy.credentials).toBe(false)
expect(policy.headers).toContain('X-Run-Id')
expect(policy.headers).toContain('X-Sim-Stream-Protocol')
expect(policy.headers).not.toContain('X-Execution-Id')
// Async is body-selected on v2 — the mode header is deliberately absent.
expect(policy.headers).not.toContain('X-Execution-Mode')
})
it('does not match the v2 execute rule for nested or runs paths', () => {
const nested = resolveApiCorsPolicy(
makeRequest('/api/v2/workflows/workflow-123/execute/extra', 'https://other.example')
)
expect(nested.origin).toBe('https://app.sim.test')
const runs = resolveApiCorsPolicy(
makeRequest('/api/v2/workflows/workflow-123/runs/run-1', 'https://other.example')
)
expect(runs.origin).toBe('https://app.sim.test')
})
it('does not match the workflow execute rule for nested paths', () => {
const policy = resolveApiCorsPolicy(
makeRequest('/api/workflows/workflow-123/execute/extra', 'https://other.example')
)
expect(policy.origin).toBe('https://app.sim.test')
})
it('returns default policy with APP_URL and credentials for other API routes', () => {
const policy = resolveApiCorsPolicy(makeRequest('/api/files/uploads'))
expect(policy).toEqual({
origin: 'https://app.sim.test',
credentials: true,
methods: 'GET,POST,OPTIONS,PUT,DELETE',
headers: expect.stringContaining('Authorization'),
})
})
it('never pairs wildcard origin with credentials (CORS spec invariant)', () => {
const paths = [
'/api/auth/oauth2/token',
'/api/mcp/copilot',
'/api/chat/abc',
'/api/workflows/wf/execute',
'/api/v2/workflows/wf/execute',
'/api/files/uploads',
]
for (const path of paths) {
const policy = resolveApiCorsPolicy(makeRequest(path))
if (policy.origin === '*') {
expect(policy.credentials).toBe(false)
}
}
})
})