@agent-native/core 0.15.6 → 0.15.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +19 -1
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +9 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +82 -11
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.spec.js +43 -1
- package/dist/client/settings/useBuilderStatus.spec.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +5 -2
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/builder-browser.d.ts +39 -11
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +182 -17
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +72 -13
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/credential-provider.d.ts +25 -4
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +81 -5
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/server/google-realtime-session.d.ts.map +1 -1
- package/dist/server/google-realtime-session.js +1 -1
- package/dist/server/google-realtime-session.js.map +1 -1
- package/package.json +1 -1
|
@@ -22,7 +22,11 @@ export interface BuilderBrowserStatus {
|
|
|
22
22
|
* account, which takes precedence for their request.
|
|
23
23
|
*/
|
|
24
24
|
envManaged: boolean;
|
|
25
|
-
credentialSource?: "user" | "org" | "env";
|
|
25
|
+
credentialSource?: "user" | "org" | "workspace" | "env";
|
|
26
|
+
connectError?: {
|
|
27
|
+
message: string;
|
|
28
|
+
at: number;
|
|
29
|
+
};
|
|
26
30
|
appHost: string;
|
|
27
31
|
apiHost: string;
|
|
28
32
|
/**
|
|
@@ -75,6 +79,18 @@ export declare function getBuilderBranchProjectId(): string;
|
|
|
75
79
|
export declare function isBuilderBranchingEnabled(): boolean;
|
|
76
80
|
export declare function resolveBuilderBranchProjectId(): Promise<string>;
|
|
77
81
|
export declare function resolveIsBuilderBranchingEnabled(): Promise<boolean>;
|
|
82
|
+
/**
|
|
83
|
+
* Query param on the callback URL that carries the original preview opener
|
|
84
|
+
* origin when cli-auth's allow-list forces `preview_url` to the gateway.
|
|
85
|
+
* Read on the callback to derive the correct postMessage targetOrigin.
|
|
86
|
+
*
|
|
87
|
+
* Not signed: the receive-side trust check in `useBuilderStatus` still
|
|
88
|
+
* gates messages by allow-listed origin. The worst an attacker could do by
|
|
89
|
+
* crafting a different `_an_opener` value is target a postMessage to an
|
|
90
|
+
* origin that doesn't match the actual opener — postMessage drops the
|
|
91
|
+
* message in that case, identical to the legacy wildcard-fallback path.
|
|
92
|
+
*/
|
|
93
|
+
export declare const BUILDER_OPENER_PARAM = "_an_opener";
|
|
78
94
|
/**
|
|
79
95
|
* Build the Builder cli-auth URL for the connect popup. When a signed
|
|
80
96
|
* `state` token is supplied it is embedded inside the `redirect_url`
|
|
@@ -83,12 +99,13 @@ export declare function resolveIsBuilderBranchingEnabled(): Promise<boolean>;
|
|
|
83
99
|
* api-key / etc., so we don't depend on Builder echoing a top-level
|
|
84
100
|
* `state` parameter (it doesn't).
|
|
85
101
|
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* fresh state bound to the current session on every click.
|
|
102
|
+
* Status responses can surface this URL directly; the legacy
|
|
103
|
+
* `/_agent-native/builder/connect` trampoline still calls this helper for
|
|
104
|
+
* clients that only know the app-local connect URL.
|
|
90
105
|
*/
|
|
91
|
-
export declare function buildBuilderCliAuthUrl(
|
|
106
|
+
export declare function buildBuilderCliAuthUrl(callbackOrigin: string, state?: string | null, options?: {
|
|
107
|
+
previewOrigin?: string;
|
|
108
|
+
}): string;
|
|
92
109
|
/**
|
|
93
110
|
* The bare URL surfaced to clients as `connectUrl`. The status route appends
|
|
94
111
|
* a short-lived signed connect token when it knows the current owner; this
|
|
@@ -97,12 +114,20 @@ export declare function buildBuilderCliAuthUrl(origin: string, state?: string |
|
|
|
97
114
|
*/
|
|
98
115
|
export declare function getBuilderBrowserConnectUrl(origin: string): string;
|
|
99
116
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* the same deployment that minted them.
|
|
117
|
+
* User-visible Builder connect origin. In Builder/Fusion previews, keep the
|
|
118
|
+
* connect URL on the actual app preview origin so clicking Connect happens in
|
|
119
|
+
* the same deployment that minted the signed connect token.
|
|
104
120
|
*/
|
|
105
121
|
export declare function getBuilderBrowserOriginForEvent(event: H3Event): string;
|
|
122
|
+
/**
|
|
123
|
+
* Builder's /cli-auth page currently only accepts localhost, *.builder.io,
|
|
124
|
+
* *.agent-native.com, or builder: redirect_url destinations. Preview hosts
|
|
125
|
+
* such as *.builderio.xyz and *.builder.codes are valid app origins for us,
|
|
126
|
+
* but Builder rejects them and falls back to http://localhost:10110/auth.
|
|
127
|
+
* Use a configured public gateway for the callback in those cases while
|
|
128
|
+
* leaving the surfaced connect URL on the user's active preview.
|
|
129
|
+
*/
|
|
130
|
+
export declare function getBuilderCliAuthCallbackOriginForEvent(event: H3Event): string;
|
|
106
131
|
export declare function getBuilderBrowserStatus(origin: string): BuilderBrowserStatus;
|
|
107
132
|
export declare function getBuilderBrowserStatusForEvent(event: H3Event): BuilderBrowserStatus;
|
|
108
133
|
/**
|
|
@@ -125,7 +150,9 @@ export declare function getBuilderCallbackEnvVars(params: {
|
|
|
125
150
|
value: string;
|
|
126
151
|
}[];
|
|
127
152
|
export declare function resolveSafePreviewUrl(previewUrl: string | null | undefined, event: H3Event): string;
|
|
128
|
-
export declare function createBuilderBrowserCallbackPage(previewUrl: string
|
|
153
|
+
export declare function createBuilderBrowserCallbackPage(previewUrl: string, opts?: {
|
|
154
|
+
parentOrigin?: string;
|
|
155
|
+
}): string;
|
|
129
156
|
/**
|
|
130
157
|
* HTML page rendered inside the OAuth popup when the callback handler caught
|
|
131
158
|
* an error persisting the per-user Builder credentials. Without this, the
|
|
@@ -144,6 +171,7 @@ export declare function createBuilderBrowserCallbackErrorPage(message: string, o
|
|
|
144
171
|
title?: string;
|
|
145
172
|
body?: string;
|
|
146
173
|
closeHint?: string;
|
|
174
|
+
parentOrigin?: string;
|
|
147
175
|
}): string;
|
|
148
176
|
export interface RunBuilderAgentArgs {
|
|
149
177
|
prompt: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder-browser.d.ts","sourceRoot":"","sources":["../../src/server/builder-browser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAUlC,eAAO,MAAM,qBAAqB,oCAAoC,CAAC;AAmBvE;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,cAAc,CAAC;AAC/C,eAAO,MAAM,qBAAqB,gBAAgB,CAAC;AACnD,eAAO,MAAM,4BAA4B,6BAA6B,CAAC;AAIvE,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,yBAAyB,EAAE,OAAO,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"builder-browser.d.ts","sourceRoot":"","sources":["../../src/server/builder-browser.ts"],"names":[],"mappings":"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"}
|
|
@@ -165,7 +165,16 @@ function isAllowedBrowserReturnUrl(urlString) {
|
|
|
165
165
|
const isLocalhost = hostname === "localhost" ||
|
|
166
166
|
hostname === "127.0.0.1" ||
|
|
167
167
|
hostname === "[::1]";
|
|
168
|
-
const isBuilderDomain = hostname === "builder.io" ||
|
|
168
|
+
const isBuilderDomain = hostname === "builder.io" ||
|
|
169
|
+
hostname.endsWith(".builder.io") ||
|
|
170
|
+
hostname === "builder.my" ||
|
|
171
|
+
hostname.endsWith(".builder.my") ||
|
|
172
|
+
hostname === "builderio.xyz" ||
|
|
173
|
+
hostname.endsWith(".builderio.xyz") ||
|
|
174
|
+
hostname === "builderio.dev" ||
|
|
175
|
+
hostname.endsWith(".builderio.dev") ||
|
|
176
|
+
hostname === "builder.codes" ||
|
|
177
|
+
hostname.endsWith(".builder.codes");
|
|
169
178
|
const isAgentNativeDomain = hostname === "agent-native.com" || hostname.endsWith(".agent-native.com");
|
|
170
179
|
return (isAllowedProtocol &&
|
|
171
180
|
(isLocalhost || isBuilderDomain || isAgentNativeDomain));
|
|
@@ -224,6 +233,72 @@ export async function resolveBuilderBranchProjectId() {
|
|
|
224
233
|
export async function resolveIsBuilderBranchingEnabled() {
|
|
225
234
|
return !!(await resolveBuilderBranchProjectId());
|
|
226
235
|
}
|
|
236
|
+
function isBuilderCliAuthAllowedOrigin(origin) {
|
|
237
|
+
if (!origin)
|
|
238
|
+
return false;
|
|
239
|
+
try {
|
|
240
|
+
const parsed = new URL(origin);
|
|
241
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
242
|
+
const isAllowedProtocol = parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
243
|
+
const isLocalhost = hostname === "localhost" ||
|
|
244
|
+
hostname === "127.0.0.1" ||
|
|
245
|
+
hostname === "::1" ||
|
|
246
|
+
hostname === "[::1]";
|
|
247
|
+
const isBuilderDomain = hostname === "builder.io" || hostname.endsWith(".builder.io");
|
|
248
|
+
const isAgentNativeDomain = hostname === "agent-native.com" || hostname.endsWith(".agent-native.com");
|
|
249
|
+
return (isAllowedProtocol &&
|
|
250
|
+
(isLocalhost || isBuilderDomain || isAgentNativeDomain));
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
function firstBuilderCliAuthCallbackOriginFromEnv() {
|
|
257
|
+
for (const key of [
|
|
258
|
+
"APP_URL",
|
|
259
|
+
"VITE_APP_URL",
|
|
260
|
+
"BETTER_AUTH_URL",
|
|
261
|
+
"VITE_BETTER_AUTH_URL",
|
|
262
|
+
"WORKSPACE_GATEWAY_URL",
|
|
263
|
+
"VITE_WORKSPACE_GATEWAY_URL",
|
|
264
|
+
]) {
|
|
265
|
+
const raw = process.env[key];
|
|
266
|
+
if (!raw)
|
|
267
|
+
continue;
|
|
268
|
+
try {
|
|
269
|
+
const origin = new URL(raw).origin;
|
|
270
|
+
if (isBuilderCliAuthAllowedOrigin(origin))
|
|
271
|
+
return origin;
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
// Ignore malformed environment values.
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Query param on the callback URL that carries the original preview opener
|
|
281
|
+
* origin when cli-auth's allow-list forces `preview_url` to the gateway.
|
|
282
|
+
* Read on the callback to derive the correct postMessage targetOrigin.
|
|
283
|
+
*
|
|
284
|
+
* Not signed: the receive-side trust check in `useBuilderStatus` still
|
|
285
|
+
* gates messages by allow-listed origin. The worst an attacker could do by
|
|
286
|
+
* crafting a different `_an_opener` value is target a postMessage to an
|
|
287
|
+
* origin that doesn't match the actual opener — postMessage drops the
|
|
288
|
+
* message in that case, identical to the legacy wildcard-fallback path.
|
|
289
|
+
*/
|
|
290
|
+
export const BUILDER_OPENER_PARAM = "_an_opener";
|
|
291
|
+
function isBuilderOpenerOriginSafe(value) {
|
|
292
|
+
if (!value)
|
|
293
|
+
return false;
|
|
294
|
+
try {
|
|
295
|
+
const parsed = new URL(value);
|
|
296
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
227
302
|
/**
|
|
228
303
|
* Build the Builder cli-auth URL for the connect popup. When a signed
|
|
229
304
|
* `state` token is supplied it is embedded inside the `redirect_url`
|
|
@@ -232,24 +307,38 @@ export async function resolveIsBuilderBranchingEnabled() {
|
|
|
232
307
|
* api-key / etc., so we don't depend on Builder echoing a top-level
|
|
233
308
|
* `state` parameter (it doesn't).
|
|
234
309
|
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
* fresh state bound to the current session on every click.
|
|
310
|
+
* Status responses can surface this URL directly; the legacy
|
|
311
|
+
* `/_agent-native/builder/connect` trampoline still calls this helper for
|
|
312
|
+
* clients that only know the app-local connect URL.
|
|
239
313
|
*/
|
|
240
|
-
export function buildBuilderCliAuthUrl(
|
|
241
|
-
const
|
|
314
|
+
export function buildBuilderCliAuthUrl(callbackOrigin, state = null, options = {}) {
|
|
315
|
+
const normalizedCallbackOrigin = normalizeOrigin(callbackOrigin);
|
|
316
|
+
const requestedPreviewOrigin = normalizeOrigin(options.previewOrigin || callbackOrigin);
|
|
317
|
+
const normalizedPreviewOrigin = isBuilderCliAuthAllowedOrigin(requestedPreviewOrigin)
|
|
318
|
+
? requestedPreviewOrigin
|
|
319
|
+
: normalizedCallbackOrigin;
|
|
242
320
|
const appBasePath = getAppBasePath();
|
|
243
|
-
const callbackUrl = new URL(`${appBasePath}${BUILDER_CALLBACK_PATH}`,
|
|
321
|
+
const callbackUrl = new URL(`${appBasePath}${BUILDER_CALLBACK_PATH}`, normalizedCallbackOrigin);
|
|
244
322
|
if (state) {
|
|
245
323
|
callbackUrl.searchParams.set(BUILDER_STATE_PARAM, state);
|
|
246
324
|
}
|
|
325
|
+
// When the cli-auth allow-list forces preview_url onto the gateway origin,
|
|
326
|
+
// the callback would otherwise lose the real opener origin and post its
|
|
327
|
+
// success message to the gateway instead of the preview tab. Embed the
|
|
328
|
+
// original preview origin in the callback's own query string so the
|
|
329
|
+
// callback handler can recover it for parentOrigin / postMessage. Builder
|
|
330
|
+
// preserves the redirect_url's query verbatim, so this round-trips.
|
|
331
|
+
if (requestedPreviewOrigin &&
|
|
332
|
+
requestedPreviewOrigin !== normalizedPreviewOrigin &&
|
|
333
|
+
isBuilderOpenerOriginSafe(requestedPreviewOrigin)) {
|
|
334
|
+
callbackUrl.searchParams.set(BUILDER_OPENER_PARAM, requestedPreviewOrigin);
|
|
335
|
+
}
|
|
247
336
|
const url = new URL("/cli-auth", getBuilderAppHost());
|
|
248
337
|
url.searchParams.set("response_type", "code");
|
|
249
338
|
url.searchParams.set("host", BUILDER_BROWSER_HOST);
|
|
250
339
|
url.searchParams.set("client_id", BUILDER_BROWSER_CLIENT_ID);
|
|
251
340
|
url.searchParams.set("redirect_url", callbackUrl.toString());
|
|
252
|
-
url.searchParams.set("preview_url", `${
|
|
341
|
+
url.searchParams.set("preview_url", `${normalizedPreviewOrigin}${appBasePath}`);
|
|
253
342
|
url.searchParams.set("framework", "agent-native");
|
|
254
343
|
return url.toString();
|
|
255
344
|
}
|
|
@@ -301,17 +390,61 @@ function isTrustedBuilderRequestHost(host) {
|
|
|
301
390
|
return false;
|
|
302
391
|
}
|
|
303
392
|
}
|
|
393
|
+
function isLoopbackBuilderRequestHost(host) {
|
|
394
|
+
if (!host)
|
|
395
|
+
return false;
|
|
396
|
+
try {
|
|
397
|
+
const hostname = new URL(`http://${host}`).hostname.toLowerCase();
|
|
398
|
+
return (hostname === "localhost" ||
|
|
399
|
+
hostname === "127.0.0.1" ||
|
|
400
|
+
hostname === "::1" ||
|
|
401
|
+
hostname === "[::1]");
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
function firstPublicBuilderPreviewOriginFromEnv() {
|
|
408
|
+
for (const key of [
|
|
409
|
+
"FUSION_ENV_ORIGIN",
|
|
410
|
+
"VITE_FUSION_ENV_ORIGIN",
|
|
411
|
+
"BUILDER_PREVIEW_URL",
|
|
412
|
+
"VITE_BUILDER_PREVIEW_URL",
|
|
413
|
+
]) {
|
|
414
|
+
const raw = process.env[key];
|
|
415
|
+
if (!raw)
|
|
416
|
+
continue;
|
|
417
|
+
try {
|
|
418
|
+
const url = new URL(raw);
|
|
419
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
420
|
+
continue;
|
|
421
|
+
if (isLoopbackBuilderRequestHost(url.host))
|
|
422
|
+
continue;
|
|
423
|
+
if (!isTrustedBuilderRequestHost(url.host))
|
|
424
|
+
continue;
|
|
425
|
+
return url.origin;
|
|
426
|
+
}
|
|
427
|
+
catch {
|
|
428
|
+
// Ignore malformed environment values.
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
304
433
|
/**
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
* the same deployment that minted them.
|
|
434
|
+
* User-visible Builder connect origin. In Builder/Fusion previews, keep the
|
|
435
|
+
* connect URL on the actual app preview origin so clicking Connect happens in
|
|
436
|
+
* the same deployment that minted the signed connect token.
|
|
309
437
|
*/
|
|
310
438
|
export function getBuilderBrowserOriginForEvent(event) {
|
|
311
439
|
const headerHost = firstHeaderValue(readEventHeader(event, "x-forwarded-host") ||
|
|
312
440
|
readEventHeader(event, "host"));
|
|
313
441
|
if (!isTrustedBuilderRequestHost(headerHost))
|
|
314
442
|
return getOrigin(event);
|
|
443
|
+
if (isLoopbackBuilderRequestHost(headerHost)) {
|
|
444
|
+
const publicPreviewOrigin = firstPublicBuilderPreviewOriginFromEnv();
|
|
445
|
+
if (publicPreviewOrigin)
|
|
446
|
+
return publicPreviewOrigin;
|
|
447
|
+
}
|
|
315
448
|
const rawProto = firstHeaderValue(readEventHeader(event, "x-forwarded-proto"));
|
|
316
449
|
const proto = rawProto === "http" || rawProto === "https"
|
|
317
450
|
? rawProto
|
|
@@ -320,6 +453,20 @@ export function getBuilderBrowserOriginForEvent(event) {
|
|
|
320
453
|
: "http";
|
|
321
454
|
return `${proto}://${headerHost}`;
|
|
322
455
|
}
|
|
456
|
+
/**
|
|
457
|
+
* Builder's /cli-auth page currently only accepts localhost, *.builder.io,
|
|
458
|
+
* *.agent-native.com, or builder: redirect_url destinations. Preview hosts
|
|
459
|
+
* such as *.builderio.xyz and *.builder.codes are valid app origins for us,
|
|
460
|
+
* but Builder rejects them and falls back to http://localhost:10110/auth.
|
|
461
|
+
* Use a configured public gateway for the callback in those cases while
|
|
462
|
+
* leaving the surfaced connect URL on the user's active preview.
|
|
463
|
+
*/
|
|
464
|
+
export function getBuilderCliAuthCallbackOriginForEvent(event) {
|
|
465
|
+
const previewOrigin = getBuilderBrowserOriginForEvent(event);
|
|
466
|
+
if (isBuilderCliAuthAllowedOrigin(previewOrigin))
|
|
467
|
+
return previewOrigin;
|
|
468
|
+
return firstBuilderCliAuthCallbackOriginFromEnv() ?? previewOrigin;
|
|
469
|
+
}
|
|
323
470
|
export function getBuilderBrowserStatus(origin) {
|
|
324
471
|
const branchProjectId = getConfiguredBuilderBranchProjectId();
|
|
325
472
|
const envManaged = !!process.env.BUILDER_PRIVATE_KEY;
|
|
@@ -371,7 +518,7 @@ export function resolveSafePreviewUrl(previewUrl, event) {
|
|
|
371
518
|
if (previewUrl && isAllowedBrowserReturnUrl(previewUrl)) {
|
|
372
519
|
return previewUrl;
|
|
373
520
|
}
|
|
374
|
-
return
|
|
521
|
+
return getBuilderBrowserOriginForEvent(event);
|
|
375
522
|
}
|
|
376
523
|
/**
|
|
377
524
|
* Inline theme-detection script that runs before the body paints. Reads the
|
|
@@ -517,8 +664,24 @@ const BUILDER_CALLBACK_BASE_CSS = `
|
|
|
517
664
|
word-break: break-word;
|
|
518
665
|
}
|
|
519
666
|
`;
|
|
520
|
-
|
|
667
|
+
function safeOriginFromUrl(value) {
|
|
668
|
+
if (!value)
|
|
669
|
+
return null;
|
|
670
|
+
try {
|
|
671
|
+
return new URL(value).origin;
|
|
672
|
+
}
|
|
673
|
+
catch {
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
export function createBuilderBrowserCallbackPage(previewUrl, opts = {}) {
|
|
521
678
|
const escapedUrl = JSON.stringify(previewUrl);
|
|
679
|
+
const parentOrigin = safeOriginFromUrl(opts.parentOrigin) ?? safeOriginFromUrl(previewUrl);
|
|
680
|
+
// postMessage requires a specific target origin for cross-origin opener
|
|
681
|
+
// delivery; only fall back to "*" when we have no usable origin (the
|
|
682
|
+
// BroadcastChannel path on the success page still works for same-origin
|
|
683
|
+
// openers in that case).
|
|
684
|
+
const escapedTargetOrigin = JSON.stringify(parentOrigin ?? "*");
|
|
522
685
|
return `<!doctype html>
|
|
523
686
|
<html lang="en">
|
|
524
687
|
<head>
|
|
@@ -561,7 +724,7 @@ export function createBuilderBrowserCallbackPage(previewUrl) {
|
|
|
561
724
|
if (window.opener && !window.opener.closed) {
|
|
562
725
|
window.opener.postMessage(
|
|
563
726
|
{ type: "builder-connect-success" },
|
|
564
|
-
|
|
727
|
+
${escapedTargetOrigin},
|
|
565
728
|
);
|
|
566
729
|
}
|
|
567
730
|
} catch (e) {}
|
|
@@ -597,6 +760,8 @@ export function createBuilderBrowserCallbackPage(previewUrl) {
|
|
|
597
760
|
*/
|
|
598
761
|
export function createBuilderBrowserCallbackErrorPage(message, opts = {}) {
|
|
599
762
|
const escapedMessage = JSON.stringify(message);
|
|
763
|
+
const parentOrigin = safeOriginFromUrl(opts.parentOrigin);
|
|
764
|
+
const escapedTargetOrigin = JSON.stringify(parentOrigin ?? "*");
|
|
600
765
|
const title = opts.title ?? "Couldn't save Builder connection";
|
|
601
766
|
const body = opts.body ??
|
|
602
767
|
"Builder authorized your account but the server couldn't persist the credentials.";
|
|
@@ -645,7 +810,7 @@ export function createBuilderBrowserCallbackErrorPage(message, opts = {}) {
|
|
|
645
810
|
try {
|
|
646
811
|
window.opener.postMessage(
|
|
647
812
|
{ type: "builder-connect-error", message: msg },
|
|
648
|
-
|
|
813
|
+
${escapedTargetOrigin},
|
|
649
814
|
);
|
|
650
815
|
} catch (e) {}
|
|
651
816
|
}
|