@shopgate/engage 7.32.0-beta.12 → 7.32.0-beta.14
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/admin-preview/constants/index.d.ts +9 -1
- package/admin-preview/constants/index.d.ts.map +1 -1
- package/admin-preview/constants/index.js +10 -2
- package/admin-preview/helpers/_internal/origins.d.ts +20 -0
- package/admin-preview/helpers/_internal/origins.d.ts.map +1 -0
- package/admin-preview/helpers/_internal/origins.js +72 -0
- package/admin-preview/helpers/index.d.ts +13 -0
- package/admin-preview/helpers/index.d.ts.map +1 -1
- package/admin-preview/helpers/index.js +17 -1
- package/admin-preview/hooks/index.d.ts +9 -7
- package/admin-preview/hooks/index.d.ts.map +1 -1
- package/admin-preview/hooks/index.js +21 -18
- package/admin-preview/initialization/awaitInitialFrontendSettings.d.ts +4 -3
- package/admin-preview/initialization/awaitInitialFrontendSettings.d.ts.map +1 -1
- package/admin-preview/initialization/awaitInitialFrontendSettings.js +13 -9
- package/package.json +7 -7
- package/page/components/Widgets/hooks.d.ts.map +1 -1
- package/page/components/Widgets/hooks.js +5 -1
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
export declare const PAGE_PREVIEW_PATTERN = "/shopgate-internal-page-preview";
|
|
2
2
|
/**
|
|
3
|
-
* List of allowed
|
|
3
|
+
* List of allowed origin patterns for admin preview iFrame communication. Both directions are
|
|
4
|
+
* validated against these patterns: incoming messages are only processed when their origin matches,
|
|
5
|
+
* and outgoing messages are only posted to a matching origin - either the origin of the last
|
|
6
|
+
* accepted incoming message, or the origin of the embedding document.
|
|
7
|
+
*
|
|
8
|
+
* A "*" acts as a wildcard for one or more domain labels, so "https://*.shopgate.com" matches
|
|
9
|
+
* "https://app.shopgate.com" as well as "https://next.us.admin.shopgate.com", but neither
|
|
10
|
+
* "https://shopgate.com" nor "https://evil.shopgate.com.attacker.example". Every other character
|
|
11
|
+
* is matched literally, which means that scheme and port always have to match exactly.
|
|
4
12
|
*/
|
|
5
13
|
export declare const ALLOWED_ADMIN_PREVIEW_ORIGINS: string[];
|
|
6
14
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../admin-preview/constants/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,oCAAoC,CAAC;AAEtE
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../admin-preview/constants/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,oCAAoC,CAAC;AAEtE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,6BAA6B,UAMzC,CAAC"}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
export const PAGE_PREVIEW_PATTERN = '/shopgate-internal-page-preview';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* List of allowed
|
|
4
|
+
* List of allowed origin patterns for admin preview iFrame communication. Both directions are
|
|
5
|
+
* validated against these patterns: incoming messages are only processed when their origin matches,
|
|
6
|
+
* and outgoing messages are only posted to a matching origin - either the origin of the last
|
|
7
|
+
* accepted incoming message, or the origin of the embedding document.
|
|
8
|
+
*
|
|
9
|
+
* A "*" acts as a wildcard for one or more domain labels, so "https://*.shopgate.com" matches
|
|
10
|
+
* "https://app.shopgate.com" as well as "https://next.us.admin.shopgate.com", but neither
|
|
11
|
+
* "https://shopgate.com" nor "https://evil.shopgate.com.attacker.example". Every other character
|
|
12
|
+
* is matched literally, which means that scheme and port always have to match exactly.
|
|
5
13
|
*/
|
|
6
|
-
export const ALLOWED_ADMIN_PREVIEW_ORIGINS = ['https
|
|
14
|
+
export const ALLOWED_ADMIN_PREVIEW_ORIGINS = ['https://*.shopgate.com', 'https://*.shopgatedev.com', 'https://*.shopgatepg.com', 'http://localhost.localdev.cc', 'http://localhost:1337'];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal to the admin-preview package - do not import this module from anywhere else. Consume
|
|
3
|
+
* the isAllowedOrigin / getReferrerOrigin helpers from @shopgate/engage/admin-preview/helpers
|
|
4
|
+
* instead.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Checks if an origin is covered by a list of allowed origin patterns.
|
|
8
|
+
* @param origin The origin to check e.g. "https://app.shopgate.com".
|
|
9
|
+
* @param patterns The allowed origin patterns.
|
|
10
|
+
* @returns Whether the origin is allowed.
|
|
11
|
+
*/
|
|
12
|
+
export declare const matchesAllowedOrigin: (origin: string | null | undefined, patterns?: string[]) => origin is string;
|
|
13
|
+
/**
|
|
14
|
+
* Determines the origin of the document that embeds the current page. Since allowed origins can be
|
|
15
|
+
* patterns, and patterns are no valid postMessage targets, this is the only way to address a parent
|
|
16
|
+
* that has not sent a message yet.
|
|
17
|
+
* @returns The referrer origin, or null when it can't be determined.
|
|
18
|
+
*/
|
|
19
|
+
export declare const readReferrerOrigin: () => string | null;
|
|
20
|
+
//# sourceMappingURL=origins.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"origins.d.ts","sourceRoot":"","sources":["../../../../admin-preview/helpers/_internal/origins.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAwBH;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,MAAM,GAAG,IAAI,GAAG,SAAS,EACjC,WAAU,MAAM,EAAO,KACtB,MAAM,IAAI,MAwBZ,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,QAAO,MAAM,GAAG,IAM9C,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import "core-js/modules/es.array.includes.js";
|
|
2
|
+
import "core-js/modules/es.string.replace.js";
|
|
3
|
+
import "core-js/modules/web.url.js";
|
|
4
|
+
import "core-js/modules/web.url.to-json.js";
|
|
5
|
+
import "core-js/modules/web.url-search-params.js";
|
|
6
|
+
/**
|
|
7
|
+
* Internal to the admin-preview package - do not import this module from anywhere else. Consume
|
|
8
|
+
* the isAllowedOrigin / getReferrerOrigin helpers from @shopgate/engage/admin-preview/helpers
|
|
9
|
+
* instead.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Regular expression source that a "*" within an allowed origin pattern is replaced with. It
|
|
14
|
+
* matches one or more domain labels, but never a dot at the very end, so that the suffix of the
|
|
15
|
+
* pattern stays anchored to the end of the origin.
|
|
16
|
+
*/
|
|
17
|
+
const ORIGIN_WILDCARD_SOURCE = '(?:[a-zA-Z0-9-]+\\.)*[a-zA-Z0-9-]+';
|
|
18
|
+
|
|
19
|
+
// Cache for regular expressions that were already created from an origin pattern.
|
|
20
|
+
const originPatternCache = new Map();
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Converts an allowed origin pattern into an anchored regular expression. Every character except
|
|
24
|
+
* the "*" wildcard is matched literally.
|
|
25
|
+
* @param pattern The origin pattern e.g. "https://*.shopgate.com".
|
|
26
|
+
* @returns The regular expression for the pattern.
|
|
27
|
+
*/
|
|
28
|
+
const createOriginRegExp = pattern => {
|
|
29
|
+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
30
|
+
return new RegExp(`^${escaped.split('\\*').join(ORIGIN_WILDCARD_SOURCE)}$`);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Checks if an origin is covered by a list of allowed origin patterns.
|
|
35
|
+
* @param origin The origin to check e.g. "https://app.shopgate.com".
|
|
36
|
+
* @param patterns The allowed origin patterns.
|
|
37
|
+
* @returns Whether the origin is allowed.
|
|
38
|
+
*/
|
|
39
|
+
export const matchesAllowedOrigin = (origin, patterns = []) => {
|
|
40
|
+
// Opaque origins are serialized as "null" and must never be trusted.
|
|
41
|
+
if (typeof origin !== 'string' || origin === '' || origin === 'null') {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
return patterns.some(pattern => {
|
|
45
|
+
if (typeof pattern !== 'string' || pattern === '') {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
if (!pattern.includes('*')) {
|
|
49
|
+
return pattern === origin;
|
|
50
|
+
}
|
|
51
|
+
let regExp = originPatternCache.get(pattern);
|
|
52
|
+
if (!regExp) {
|
|
53
|
+
regExp = createOriginRegExp(pattern);
|
|
54
|
+
originPatternCache.set(pattern, regExp);
|
|
55
|
+
}
|
|
56
|
+
return regExp.test(origin);
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Determines the origin of the document that embeds the current page. Since allowed origins can be
|
|
62
|
+
* patterns, and patterns are no valid postMessage targets, this is the only way to address a parent
|
|
63
|
+
* that has not sent a message yet.
|
|
64
|
+
* @returns The referrer origin, or null when it can't be determined.
|
|
65
|
+
*/
|
|
66
|
+
export const readReferrerOrigin = () => {
|
|
67
|
+
try {
|
|
68
|
+
return new URL(document.referrer).origin;
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
@@ -20,4 +20,17 @@ export declare const isFrontendSettingsAdminPreviewActive: () => boolean;
|
|
|
20
20
|
* @returns True if the app must not navigate, false otherwise.
|
|
21
21
|
*/
|
|
22
22
|
export declare const isNavigationBlocked: () => boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Checks if an origin is covered by a list of allowed origin patterns. See
|
|
25
|
+
* ALLOWED_ADMIN_PREVIEW_ORIGINS for details about the supported wildcard syntax.
|
|
26
|
+
* @param origin The origin to check e.g. "https://app.shopgate.com".
|
|
27
|
+
* @param patterns The allowed origin patterns.
|
|
28
|
+
* @returns Whether the origin is allowed.
|
|
29
|
+
*/
|
|
30
|
+
export declare const isAllowedOrigin: (origin: string | null | undefined, patterns?: string[]) => origin is string;
|
|
31
|
+
/**
|
|
32
|
+
* Determines the origin of the document that embeds the current page.
|
|
33
|
+
* @returns The referrer origin, or null when it can't be determined.
|
|
34
|
+
*/
|
|
35
|
+
export declare const getReferrerOrigin: () => string | null;
|
|
23
36
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../admin-preview/helpers/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../admin-preview/helpers/index.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,eAAO,MAAM,oBAAoB,QAAO,OAClB,CAAC;AAEvB;;;GAGG;AACH,eAAO,MAAM,wBAAwB,QAAO,OAClB,CAAC;AAE3B;;;GAGG;AACH,eAAO,MAAM,oCAAoC,QAAO,OAClB,CAAC;AAEvC;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,QAAO,OACX,CAAC;AAE7B;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAC1B,QAAQ,MAAM,GAAG,IAAI,GAAG,SAAS,EACjC,WAAU,MAAM,EAAO,KACtB,MAAM,IAAI,MAAgD,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAAO,MAAM,GAAG,IAA4B,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { detectAdminPreview, detectFrontendSettingsAdminPreview, detectPageAdminPreview } from "./_internal/detection";
|
|
2
|
+
import { matchesAllowedOrigin, readReferrerOrigin } from "./_internal/origins";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Checks if the app is currently in admin preview mode (pages or frontend settings).
|
|
@@ -24,4 +25,19 @@ export const isFrontendSettingsAdminPreviewActive = () => detectFrontendSettings
|
|
|
24
25
|
* Frontend settings preview is not affected - it previews styling on the regular app.
|
|
25
26
|
* @returns True if the app must not navigate, false otherwise.
|
|
26
27
|
*/
|
|
27
|
-
export const isNavigationBlocked = () => isPageAdminPreviewActive();
|
|
28
|
+
export const isNavigationBlocked = () => isPageAdminPreviewActive();
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Checks if an origin is covered by a list of allowed origin patterns. See
|
|
32
|
+
* ALLOWED_ADMIN_PREVIEW_ORIGINS for details about the supported wildcard syntax.
|
|
33
|
+
* @param origin The origin to check e.g. "https://app.shopgate.com".
|
|
34
|
+
* @param patterns The allowed origin patterns.
|
|
35
|
+
* @returns Whether the origin is allowed.
|
|
36
|
+
*/
|
|
37
|
+
export const isAllowedOrigin = (origin, patterns = []) => matchesAllowedOrigin(origin, patterns);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Determines the origin of the document that embeds the current page.
|
|
41
|
+
* @returns The referrer origin, or null when it can't be determined.
|
|
42
|
+
*/
|
|
43
|
+
export const getReferrerOrigin = () => readReferrerOrigin();
|
|
@@ -13,24 +13,26 @@ export interface IframeMessengerResult<TMessage extends MessageData = MessageDat
|
|
|
13
13
|
/**
|
|
14
14
|
* Send data up to window.parent.
|
|
15
15
|
* If targetOrigin is omitted, uses the most recently seen origin
|
|
16
|
-
* (from an incoming message)
|
|
17
|
-
*
|
|
16
|
+
* (from an incoming message), otherwise the origin derived from document.referrer.
|
|
17
|
+
* Nothing is sent when neither of them is covered by parentOrigins.
|
|
18
18
|
*/
|
|
19
19
|
sendToParent: (data: TMessage, targetOrigin?: string) => void;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Hook for postMessage communication when your component is inside an iframe.
|
|
23
23
|
*
|
|
24
|
-
* Listens on window for "message" events
|
|
25
|
-
*
|
|
24
|
+
* Listens on window for "message" events, and only calls onMessage(data, rawEvent) if the origin
|
|
25
|
+
* of the event is covered by parentOrigins and the message came from window.parent.
|
|
26
26
|
*
|
|
27
27
|
* @param onMessage Callback invoked when a trusted message arrives.
|
|
28
28
|
* Receives data and the raw event so consumers can inspect origin, source,
|
|
29
29
|
* and other metadata when needed.
|
|
30
|
-
* @param parentOrigins Array of allowed parent origin
|
|
31
|
-
* ['https://a.example.com', 'https
|
|
30
|
+
* @param parentOrigins Array of allowed parent origin patterns, e.g.
|
|
31
|
+
* ['https://a.example.com', 'https://*.example.com']. See ALLOWED_ADMIN_PREVIEW_ORIGINS for
|
|
32
|
+
* details about the supported wildcard syntax.
|
|
33
|
+
* @param enabled Whether the message listener is supposed to be attached.
|
|
32
34
|
* @returns An object with a single method:
|
|
33
35
|
* sendToParent(data, [targetOrigin]) to post data to the parent window.
|
|
34
36
|
*/
|
|
35
|
-
export declare function useIframeMessenger<TMessage extends MessageData = MessageData>(onMessage: OnIframeMessage<TMessage>, parentOrigins: string[]): IframeMessengerResult<TMessage>;
|
|
37
|
+
export declare function useIframeMessenger<TMessage extends MessageData = MessageData>(onMessage: OnIframeMessage<TMessage>, parentOrigins: string[], enabled?: boolean): IframeMessengerResult<TMessage>;
|
|
36
38
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../admin-preview/hooks/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../admin-preview/hooks/index.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,eAAe,CAAC,QAAQ,SAAS,WAAW,GAAG,WAAW,IAAI,CACxE,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,YAAY,CAAC,QAAQ,CAAC,KAC7B,IAAI,CAAC;AAEV,MAAM,WAAW,qBAAqB,CAAC,QAAQ,SAAS,WAAW,GAAG,WAAW;IAC/E;;;;;OAKG;IACH,YAAY,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/D;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,SAAS,WAAW,GAAG,WAAW,EAC3E,SAAS,EAAE,eAAe,CAAC,QAAQ,CAAC,EACpC,aAAa,EAAE,MAAM,EAAE,EACvB,OAAO,UAAO,GACb,qBAAqB,CAAC,QAAQ,CAAC,CAkEjC"}
|
|
@@ -1,24 +1,23 @@
|
|
|
1
|
-
import "core-js/modules/es.array.includes.js";
|
|
2
|
-
import "core-js/modules/web.url.js";
|
|
3
|
-
import "core-js/modules/web.url.to-json.js";
|
|
4
|
-
import "core-js/modules/web.url-search-params.js";
|
|
5
1
|
import { useCallback, useEffect, useRef } from 'react';
|
|
6
2
|
import { logger } from '@shopgate/engage/core/helpers';
|
|
3
|
+
import { getReferrerOrigin, isAllowedOrigin } from "../helpers";
|
|
7
4
|
/**
|
|
8
5
|
* Hook for postMessage communication when your component is inside an iframe.
|
|
9
6
|
*
|
|
10
|
-
* Listens on window for "message" events
|
|
11
|
-
*
|
|
7
|
+
* Listens on window for "message" events, and only calls onMessage(data, rawEvent) if the origin
|
|
8
|
+
* of the event is covered by parentOrigins and the message came from window.parent.
|
|
12
9
|
*
|
|
13
10
|
* @param onMessage Callback invoked when a trusted message arrives.
|
|
14
11
|
* Receives data and the raw event so consumers can inspect origin, source,
|
|
15
12
|
* and other metadata when needed.
|
|
16
|
-
* @param parentOrigins Array of allowed parent origin
|
|
17
|
-
* ['https://a.example.com', 'https
|
|
13
|
+
* @param parentOrigins Array of allowed parent origin patterns, e.g.
|
|
14
|
+
* ['https://a.example.com', 'https://*.example.com']. See ALLOWED_ADMIN_PREVIEW_ORIGINS for
|
|
15
|
+
* details about the supported wildcard syntax.
|
|
16
|
+
* @param enabled Whether the message listener is supposed to be attached.
|
|
18
17
|
* @returns An object with a single method:
|
|
19
18
|
* sendToParent(data, [targetOrigin]) to post data to the parent window.
|
|
20
19
|
*/
|
|
21
|
-
export function useIframeMessenger(onMessage, parentOrigins) {
|
|
20
|
+
export function useIframeMessenger(onMessage, parentOrigins, enabled = true) {
|
|
22
21
|
// Keep a ref to the latest onMessage callback so the listener always has it.
|
|
23
22
|
const onMessageRef = useRef(onMessage);
|
|
24
23
|
useEffect(() => {
|
|
@@ -31,15 +30,15 @@ export function useIframeMessenger(onMessage, parentOrigins) {
|
|
|
31
30
|
/**
|
|
32
31
|
* Send a message up to the parent window.
|
|
33
32
|
* @param data - The data object to post.
|
|
34
|
-
* @param targetOrigin Optional override for the origin to post to.
|
|
35
|
-
* If omitted, uses the last seen origin, then referrer origin
|
|
36
|
-
* then parentOrigins[0], then "*".
|
|
33
|
+
* @param targetOrigin Optional override for the origin to post to. Must be covered by
|
|
34
|
+
* parentOrigins. If omitted, uses the last seen origin, then the referrer origin.
|
|
37
35
|
*/
|
|
38
36
|
const sendToParent = useCallback((data, targetOrigin) => {
|
|
39
|
-
// Determine which origin to use: explicit, then last seen, then
|
|
40
|
-
|
|
37
|
+
// Determine which origin to use: explicit, then last seen, then the embedding document.
|
|
38
|
+
// Patterns are no valid postMessage targets, so an unresolved origin aborts the send.
|
|
39
|
+
const originToUse = [targetOrigin, lastOriginRef.current, getReferrerOrigin()].find(origin => isAllowedOrigin(origin, parentOrigins));
|
|
41
40
|
if (!originToUse) {
|
|
42
|
-
logger.warn('useIframeMessenger: no targetOrigin available. ' + 'Provide parentOrigins or pass targetOrigin.');
|
|
41
|
+
logger.warn('useIframeMessenger: no allowed targetOrigin available. ' + 'Provide parentOrigins or pass an allowed targetOrigin.');
|
|
43
42
|
return;
|
|
44
43
|
}
|
|
45
44
|
window.parent.postMessage(data, originToUse);
|
|
@@ -47,13 +46,17 @@ export function useIframeMessenger(onMessage, parentOrigins) {
|
|
|
47
46
|
|
|
48
47
|
// Attach / detach the "message" listener.
|
|
49
48
|
useEffect(() => {
|
|
49
|
+
if (!enabled) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
50
53
|
/**
|
|
51
54
|
* Handler for incoming postMessage events.
|
|
52
55
|
* @param rawEvent The original MessageEvent object.
|
|
53
56
|
*/
|
|
54
57
|
function handler(rawEvent) {
|
|
55
|
-
// Only proceed if the origin is
|
|
56
|
-
if (!
|
|
58
|
+
// Only proceed if the origin is covered by our whitelist.
|
|
59
|
+
if (!isAllowedOrigin(rawEvent.origin, parentOrigins)) return;
|
|
57
60
|
// Ensure the message actually came from window.parent.
|
|
58
61
|
if (rawEvent.source !== window.parent) return;
|
|
59
62
|
|
|
@@ -67,7 +70,7 @@ export function useIframeMessenger(onMessage, parentOrigins) {
|
|
|
67
70
|
return () => {
|
|
68
71
|
window.removeEventListener('message', handler);
|
|
69
72
|
};
|
|
70
|
-
}, [
|
|
73
|
+
}, [enabled, parentOrigins]);
|
|
71
74
|
return {
|
|
72
75
|
sendToParent
|
|
73
76
|
};
|
|
@@ -8,9 +8,10 @@ interface PreviewStore {
|
|
|
8
8
|
* listener only mounts once React committed, so everything the admin sent before that is dropped
|
|
9
9
|
* and the styling arrives as a visible restyle.
|
|
10
10
|
*
|
|
11
|
-
* The handshake goes to
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* The handshake goes to the origin of the embedding document. Nothing has been received at this
|
|
12
|
+
* point, so that referrer is the only concrete parent origin available - the allow list holds
|
|
13
|
+
* patterns, and a pattern is no valid postMessage target. An admin that strips its referrer
|
|
14
|
+
* therefore gets no ready message, and the app falls back to the timeout below.
|
|
14
15
|
*
|
|
15
16
|
* Resolves when the payload arrived or when REQUEST_TIMEOUT elapsed, and never rejects - an admin
|
|
16
17
|
* that stays silent has to fall back to the unstyled app, never to an iframe that never renders.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"awaitInitialFrontendSettings.d.ts","sourceRoot":"","sources":["../../../admin-preview/initialization/awaitInitialFrontendSettings.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"awaitInitialFrontendSettings.d.ts","sourceRoot":"","sources":["../../../admin-preview/initialization/awaitInitialFrontendSettings.ts"],"names":[],"mappings":"AAuBA,UAAU,YAAY;IACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,4BAA4B,GAAI,OAAO,YAAY,KAAG,OAAO,CAAC,IAAI,CAyE3E,CAAC"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import "core-js/modules/es.array.includes.js";
|
|
2
1
|
import { withScope, captureMessage, Severity as SentrySeverity } from '@sentry/browser';
|
|
3
2
|
import { receiveAppSettings } from '@shopgate/engage/settings/action-creators/appSettings';
|
|
4
|
-
import { isFrontendSettingsAdminPreviewActive } from "../helpers";
|
|
3
|
+
import { getReferrerOrigin, isAllowedOrigin, isFrontendSettingsAdminPreviewActive } from "../helpers";
|
|
5
4
|
import { ALLOWED_ADMIN_PREVIEW_ORIGINS } from "../constants";
|
|
6
5
|
import { getOrCreateStyleTag, serializeStyling } from "../components/FrontendSettingsPreviewBridge/helpers";
|
|
7
6
|
import { setInitialFrontendSettings } from "./_internal/initialFrontendSettings";
|
|
@@ -13,9 +12,10 @@ const REQUEST_TIMEOUT = 3000;
|
|
|
13
12
|
* listener only mounts once React committed, so everything the admin sent before that is dropped
|
|
14
13
|
* and the styling arrives as a visible restyle.
|
|
15
14
|
*
|
|
16
|
-
* The handshake goes to
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* The handshake goes to the origin of the embedding document. Nothing has been received at this
|
|
16
|
+
* point, so that referrer is the only concrete parent origin available - the allow list holds
|
|
17
|
+
* patterns, and a pattern is no valid postMessage target. An admin that strips its referrer
|
|
18
|
+
* therefore gets no ready message, and the app falls back to the timeout below.
|
|
19
19
|
*
|
|
20
20
|
* Resolves when the payload arrived or when REQUEST_TIMEOUT elapsed, and never rejects - an admin
|
|
21
21
|
* that stays silent has to fall back to the unstyled app, never to an iframe that never renders.
|
|
@@ -51,7 +51,7 @@ export const awaitInitialFrontendSettings = store => new Promise(resolve => {
|
|
|
51
51
|
* @param event The message event.
|
|
52
52
|
*/
|
|
53
53
|
function handleMessage(event) {
|
|
54
|
-
if (!
|
|
54
|
+
if (!isAllowedOrigin(event.origin, ALLOWED_ADMIN_PREVIEW_ORIGINS)) return;
|
|
55
55
|
if (event.source !== window.parent) return;
|
|
56
56
|
const {
|
|
57
57
|
data
|
|
@@ -76,9 +76,13 @@ export const awaitInitialFrontendSettings = store => new Promise(resolve => {
|
|
|
76
76
|
});
|
|
77
77
|
settle();
|
|
78
78
|
}, REQUEST_TIMEOUT);
|
|
79
|
-
|
|
79
|
+
|
|
80
|
+
// Allowed origins can be patterns, which are no valid postMessage targets, so the ready
|
|
81
|
+
// message goes to the document that embedded us - as long as it is an allowed origin.
|
|
82
|
+
const parentOrigin = getReferrerOrigin();
|
|
83
|
+
if (isAllowedOrigin(parentOrigin, ALLOWED_ADMIN_PREVIEW_ORIGINS)) {
|
|
80
84
|
window.parent.postMessage({
|
|
81
85
|
type: 'frontendSettingsPreviewReady'
|
|
82
|
-
},
|
|
83
|
-
}
|
|
86
|
+
}, parentOrigin);
|
|
87
|
+
}
|
|
84
88
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopgate/engage",
|
|
3
|
-
"version": "7.32.0-beta.
|
|
3
|
+
"version": "7.32.0-beta.14",
|
|
4
4
|
"description": "Shopgate's ENGAGE library.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Shopgate <support@shopgate.com>",
|
|
@@ -18,12 +18,12 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@emotion/react": "^11.14.0",
|
|
20
20
|
"@shopgate/native-modules": "1.0.0-beta.34",
|
|
21
|
-
"@shopgate/pwa-common": "7.32.0-beta.
|
|
22
|
-
"@shopgate/pwa-common-commerce": "7.32.0-beta.
|
|
23
|
-
"@shopgate/pwa-core": "7.32.0-beta.
|
|
24
|
-
"@shopgate/pwa-ui-ios": "7.32.0-beta.
|
|
25
|
-
"@shopgate/pwa-ui-material": "7.32.0-beta.
|
|
26
|
-
"@shopgate/pwa-ui-shared": "7.32.0-beta.
|
|
21
|
+
"@shopgate/pwa-common": "7.32.0-beta.14",
|
|
22
|
+
"@shopgate/pwa-common-commerce": "7.32.0-beta.14",
|
|
23
|
+
"@shopgate/pwa-core": "7.32.0-beta.14",
|
|
24
|
+
"@shopgate/pwa-ui-ios": "7.32.0-beta.14",
|
|
25
|
+
"@shopgate/pwa-ui-material": "7.32.0-beta.14",
|
|
26
|
+
"@shopgate/pwa-ui-shared": "7.32.0-beta.14",
|
|
27
27
|
"@stripe/react-stripe-js": "^1.16.5",
|
|
28
28
|
"@stripe/stripe-js": "^1.44.1",
|
|
29
29
|
"@virtuous/conductor": "~2.5.0",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../../page/components/Widgets/hooks.ts"],"names":[],"mappings":"AAcA,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,yBAAyB,CAAC;AAoBjC;;;GAGG;AACH,eAAO,MAAM,6BAA6B,GAAI,kBAAgB,
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../../page/components/Widgets/hooks.ts"],"names":[],"mappings":"AAcA,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,yBAAyB,CAAC;AAoBjC;;;GAGG;AACH,eAAO,MAAM,6BAA6B,GAAI,kBAAgB,SA8G7D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,QAAO,yBAA8D,CAAC"}
|
|
@@ -36,6 +36,10 @@ export const usePreviewIframeCommunication = (isActive = false) => {
|
|
|
36
36
|
const {
|
|
37
37
|
sendToParent
|
|
38
38
|
} = useIframeMessenger(data => {
|
|
39
|
+
// Allowed parents can also emit unrelated messages with arbitrary payloads.
|
|
40
|
+
if (typeof data !== 'object' || data === null) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
39
43
|
if (data.type === 'receivePageConfig') {
|
|
40
44
|
// Page preview config received from the parent window.
|
|
41
45
|
dispatch(receivePageConfigV2({
|
|
@@ -110,7 +114,7 @@ export const usePreviewIframeCommunication = (isActive = false) => {
|
|
|
110
114
|
});
|
|
111
115
|
}
|
|
112
116
|
}
|
|
113
|
-
}, ALLOWED_ADMIN_PREVIEW_ORIGINS);
|
|
117
|
+
}, ALLOWED_ADMIN_PREVIEW_ORIGINS, isActive);
|
|
114
118
|
useWidgetPreviewEvent('widget-clicked', e => {
|
|
115
119
|
if (!isActive) {
|
|
116
120
|
return;
|