Skip to content

Commit 99bb0f9

Browse files
waleedlatif1claude
andcommitted
fix(cors): scope embed CORS rule to /api/{chat,form}/[identifier] only
The embed policy (reflected origin, credentials:false) was matching workspace-internal session-authed routes — /api/chat, /api/chat/manage/*, /api/chat/validate, and the form equivalents — which need the default credentialed policy. Tighten the matcher to the embed paths only and add tests covering the exclusion. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent b2956f1 commit 99bb0f9

2 files changed

Lines changed: 40 additions & 5 deletions

File tree

apps/sim/proxy.test.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,14 @@ describe('resolveApiCorsPolicy', () => {
4545
})
4646

4747
it('reflects origin for chat and form embeds, never sets credentials', () => {
48-
for (const path of ['/api/chat/abc', '/api/form', '/api/form/xyz']) {
48+
const paths = [
49+
'/api/chat/abc',
50+
'/api/chat/abc/otp',
51+
'/api/chat/abc/sso',
52+
'/api/form/xyz',
53+
'/api/form/xyz/otp',
54+
]
55+
for (const path of paths) {
4956
const policy = resolveApiCorsPolicy(makeRequest(path, 'https://customer.example'))
5057
expect(policy).toEqual({
5158
origin: 'https://customer.example',
@@ -56,10 +63,26 @@ describe('resolveApiCorsPolicy', () => {
5663
}
5764
})
5865

59-
it('falls back to wildcard for chat/form when no origin header is present', () => {
66+
it('falls back to wildcard for chat/form embeds when no origin header is present', () => {
6067
expect(resolveApiCorsPolicy(makeRequest('/api/chat/abc')).origin).toBe('*')
6168
})
6269

70+
it('uses the default credentialed policy for workspace-internal chat/form routes', () => {
71+
const paths = [
72+
'/api/chat',
73+
'/api/chat/manage/abc',
74+
'/api/chat/validate',
75+
'/api/form',
76+
'/api/form/manage/abc',
77+
'/api/form/validate',
78+
]
79+
for (const path of paths) {
80+
const policy = resolveApiCorsPolicy(makeRequest(path, 'https://customer.example'))
81+
expect(policy.origin).toBe('https://app.sim.test')
82+
expect(policy.credentials).toBe(true)
83+
}
84+
})
85+
6386
it('serves workflow execute with wildcard origin and PUT method', () => {
6487
const policy = resolveApiCorsPolicy(
6588
makeRequest('/api/workflows/workflow-123/execute', 'https://other.example')

apps/sim/proxy.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ const DEFAULT_API_ALLOWED_HEADERS =
2222
const WORKFLOW_EXECUTE_HEADERS =
2323
'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-API-Key'
2424

25+
/**
26+
* Matches embed endpoints: /api/{chat,form}/{identifier} and subroutes
27+
* (/otp, /sso). The identifier segment explicitly excludes the
28+
* workspace-internal subpaths `manage` and `validate` so those continue
29+
* to use the default credentialed policy.
30+
*/
31+
const EMBED_PATH = /^\/api\/(chat|form)\/(?!manage(\/|$)|validate(\/|$))[^/]+(\/(otp|sso))?$/
32+
2533
interface CorsRule {
2634
match: (pathname: string) => boolean
2735
policy: (request: NextRequest) => CorsPolicy
@@ -56,9 +64,13 @@ const CORS_RULES: readonly CorsRule[] = [
5664
}),
5765
},
5866
{
59-
// Chat and form embeds run on customer domains; reflect the request
60-
// origin and omit credentials (auth uses signed tokens, not cookies).
61-
match: (p) => p === '/api/form' || p.startsWith('/api/form/') || p.startsWith('/api/chat/'),
67+
// Embed endpoints: /api/chat/[identifier] and /api/form/[identifier]
68+
// (plus their /otp and /sso subroutes). These run on customer domains —
69+
// reflect the request origin and omit credentials (auth uses signed
70+
// tokens, not cookies). Workspace-internal subpaths (`manage`, `validate`,
71+
// and the bare collection routes) are deliberately excluded so they
72+
// continue to receive the default credentialed policy.
73+
match: (p) => EMBED_PATH.test(p),
6274
policy: (request) => ({
6375
origin: request.headers.get('origin') || '*',
6476
credentials: false,

0 commit comments

Comments
 (0)