fix: send Origin header so SaaS edge WAF accepts /workspaces/* fetches
Production tenants front the workspace-server with a Cloudflare WAF that enforces same-origin on /workspaces/* paths. Without an Origin header the WAF silently re-routes the request to the canvas Next.js (which has no /workspaces page), so polls returned empty 404s and replies failed with an opaque error. Browsers set Origin automatically for cross-origin POSTs; Node/Bun fetch does not (it's a browser-only concern). Both fetch sites in this plugin hit /workspaces/* with a workspace bearer that's only valid against PLATFORM_URL anyway, so we set Origin: PLATFORM_URL explicitly — no risk of leaking the bearer to a different origin. Verified against hongmingwang.moleculesai.app: - Pre-fix: GET /workspaces/:id/activity → empty 404 (WAF re-route) - Post-fix: GET /workspaces/:id/activity → 200 [] (correct) The reply path gets the same fix; e2e verification of replies is blocked upstream (the platform's outbound forward to a registered URL needs the agent reachable from the platform's network), unrelated to this change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f3402e48ff
commit
2f08478edb
16
server.ts
16
server.ts
@ -162,7 +162,17 @@ async function pollWorkspace(workspaceId: string, mcp: Server): Promise<void> {
|
||||
let resp: Response
|
||||
try {
|
||||
resp = await fetch(url, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
// Same-origin header — required by the tenant's edge WAF on hosted
|
||||
// SaaS deployments. Without it the WAF rewrites the request and
|
||||
// /workspaces/* returns an empty 404 (it's silently routed to the
|
||||
// canvas Next.js, which has no /workspaces page). Node/Bun fetch
|
||||
// doesn't auto-set Origin (that's a browser-only concern), so we
|
||||
// set it explicitly to PLATFORM_URL — the only origin the bearer
|
||||
// is valid against anyway, so no risk of leaking it elsewhere.
|
||||
Origin: PLATFORM_URL,
|
||||
},
|
||||
signal: AbortSignal.timeout(10_000),
|
||||
})
|
||||
} catch (err) {
|
||||
@ -343,6 +353,10 @@ async function replyToWorkspace(args: z.infer<typeof ReplyArgsSchema>): Promise<
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
'X-Source-Workspace-Id': workspace_id,
|
||||
// Same-origin header for SaaS edge WAF — see pollWorkspace fetch
|
||||
// for the full explanation. /workspaces/* requires it on hosted
|
||||
// tenants; localhost ignores it.
|
||||
Origin: PLATFORM_URL,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
signal: AbortSignal.timeout(30_000),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user