@dynamic-labs/iframe-setup 4.91.5 → 4.92.0

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/CHANGELOG.md CHANGED
@@ -1,4 +1,27 @@
1
1
 
2
+ ## [4.92.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.91.6...v4.92.0) (2026-07-08)
3
+
4
+
5
+ ### Features
6
+
7
+ * **captcha:** add Cloudflare Turnstile provider support alongside hCaptcha (v4) ([#11798](https://github.com/dynamic-labs/dynamic-auth/issues/11798)) ([114c770](https://github.com/dynamic-labs/dynamic-auth/commit/114c7702e900d8dedeebe7be867de35d11a93f31))
8
+
9
+
10
+ ### Bug Fixes
11
+
12
+ * **ethereum-aa:** resolve kernel client config per wallet (v4) ([#11870](https://github.com/dynamic-labs/dynamic-auth/issues/11870)) ([1b66d79](https://github.com/dynamic-labs/dynamic-auth/commit/1b66d79b4a32b4b6de433b7536bc46e6719224c6))
13
+ * **iframe-setup:** block dangerous URL schemes and verify sender in OPEN_URL ([#11819](https://github.com/dynamic-labs/dynamic-auth/issues/11819)) ([329b27e](https://github.com/dynamic-labs/dynamic-auth/commit/329b27e9fac825916ab227ec8cf93dc343898923))
14
+ * improve social OAuth error messages ([#11837](https://github.com/dynamic-labs/dynamic-auth/issues/11837)) ([8e57c06](https://github.com/dynamic-labs/dynamic-auth/commit/8e57c067c5a89111a32a12fb82e190cf6c46ad67))
15
+ * **react-native-extension:** reject path traversal in downloadFile fileName ([#11816](https://github.com/dynamic-labs/dynamic-auth/issues/11816)) ([9367a56](https://github.com/dynamic-labs/dynamic-auth/commit/9367a56b6daa853d7f7e7f5534261bc68825b239))
16
+ * **sdk-react-core:** display exactly the bytes signed in personal_sign preview ([#11818](https://github.com/dynamic-labs/dynamic-auth/issues/11818)) ([30b9382](https://github.com/dynamic-labs/dynamic-auth/commit/30b93829122f00060cb484aef09a03294c03f651))
17
+
18
+ ### [4.91.6](https://github.com/dynamic-labs/dynamic-auth/compare/v4.91.5...v4.91.6) (2026-07-03)
19
+
20
+
21
+ ### Bug Fixes
22
+
23
+ * **react-native:** ensure webview loads on cold start ([#11787](https://github.com/dynamic-labs/dynamic-auth/issues/11787)) ([8f3dc8e](https://github.com/dynamic-labs/dynamic-auth/commit/8f3dc8e2a7de06f012ea6e922d4e76aee1c84c1a))
24
+
2
25
  ### [4.91.5](https://github.com/dynamic-labs/dynamic-auth/compare/v4.91.4...v4.91.5) (2026-07-02)
3
26
 
4
27
 
package/README.md CHANGED
@@ -146,12 +146,20 @@ export const setupIframe = (
146
146
  ): VoidFunction => {
147
147
  if (typeof window === 'undefined') return () => {};
148
148
 
149
- const messageHandler = createMessageHandler<IFRAME_EVENTS>(window, origin);
149
+ const messageHandler = createMessageHandler<IFRAME_EVENTS>(
150
+ window,
151
+ origin,
152
+ iframe,
153
+ );
150
154
  const messageSender = createMessageSender<IFRAME_EVENTS>(iframe);
151
155
 
152
156
  // Add event listener to handle deeplink
153
157
  const setupOpenUrlHandler = () =>
154
158
  messageHandler('OPEN_URL', ({ url, target = 'self', features }) => {
159
+ // This navigates the host top window, so never honor a URL whose scheme
160
+ // can execute script in the host origin (iframe->host XSS).
161
+ if (hasDangerousScheme(url)) return;
162
+
155
163
  if (target === 'blank') {
156
164
  window.open(url, '_blank', features);
157
165
  } else {
@@ -220,6 +228,45 @@ export const setupIframe = (
220
228
 
221
229
  // Utils
222
230
 
231
+ /**
232
+ * URL schemes that can execute script in the host origin. Navigating the host
233
+ * top window to any of these turns an OPEN_URL message into arbitrary code
234
+ * execution in the integrator's origin, so they are blocked.
235
+ *
236
+ * A deny-list (rather than an http/https allow-list) is used deliberately:
237
+ * wallet flows legitimately open custom-scheme deep links, and allow-listing
238
+ * http(s) would break them.
239
+ */
240
+ const DANGEROUS_SCHEMES = [
241
+ 'javascript:',
242
+ 'data:',
243
+ 'blob:',
244
+ 'vbscript:',
245
+ 'file:',
246
+ ];
247
+
248
+ /**
249
+ * Leading characters the browser ignores when resolving a URL scheme: spaces,
250
+ * tabs, newlines and other C0 control chars (U+0000–U+0020). Stripping them
251
+ * defeats obfuscation such as "\tjavascript:alert(1)".
252
+ */
253
+ // eslint-disable-next-line no-control-regex
254
+ const LEADING_IGNORED_CHARS = /^[\u0000-\u0020]+/;
255
+
256
+ /**
257
+ * Returns true when the URL begins with a script-executing scheme.
258
+ *
259
+ * Non-string input is treated as dangerous, leading ignored chars are stripped
260
+ * first, and the comparison is case-insensitive ("JavaScript:" is blocked).
261
+ */
262
+ const hasDangerousScheme = (url: unknown): boolean => {
263
+ if (typeof url !== 'string') return true;
264
+
265
+ const normalized = url.replace(LEADING_IGNORED_CHARS, '').toLowerCase();
266
+
267
+ return DANGEROUS_SCHEMES.some((scheme) => normalized.startsWith(scheme));
268
+ };
269
+
223
270
  /**
224
271
  * Custom event dispatched when the parent URL changes via the History API.
225
272
  * Used instead of re-patching history per setupIframe call so that multiple
@@ -262,7 +309,11 @@ const patchHistoryOnce = (): void => {
262
309
  * Create a message handler for the iframe
263
310
  */
264
311
  const createMessageHandler =
265
- <T extends Record<string, MESSAGE_HANDLER>>(window: Window, origin: string) =>
312
+ <T extends Record<string, MESSAGE_HANDLER>>(
313
+ window: Window,
314
+ origin: string,
315
+ iframe: HTMLIFrameElement,
316
+ ) =>
266
317
  <EVENT_NAME extends Extract<keyof T, string>>(
267
318
  eventName: EVENT_NAME,
268
319
  handler: T[EVENT_NAME],
@@ -270,6 +321,10 @@ const createMessageHandler =
270
321
  const messageHandler = (event: MessageEvent) => {
271
322
  if (event.origin !== origin) return;
272
323
 
324
+ // Only trust messages coming from this iframe's own window. Checking the
325
+ // origin alone lets any frame/tab at the SDK-iframe origin forge events.
326
+ if (event.source !== iframe.contentWindow) return;
327
+
273
328
  const { eventName: incomingEventName, args } = event.data;
274
329
 
275
330
  if (eventName === incomingEventName) {
package/package.cjs CHANGED
@@ -3,6 +3,6 @@
3
3
 
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
- var version = "4.91.5";
6
+ var version = "4.92.0";
7
7
 
8
8
  exports.version = version;
package/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  'use client'
2
- var version = "4.91.5";
2
+ var version = "4.92.0";
3
3
 
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-labs/iframe-setup",
3
- "version": "4.91.5",
3
+ "version": "4.92.0",
4
4
  "description": "Collection of utilities to use the Dynamic SDK in an iframe",
5
5
  "author": "Dynamic Labs, Inc.",
6
6
  "license": "MIT",
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "homepage": "https://www.dynamic.xyz/",
20
20
  "dependencies": {
21
- "@dynamic-labs/assert-package-version": "4.91.5"
21
+ "@dynamic-labs/assert-package-version": "4.92.0"
22
22
  },
23
23
  "peerDependencies": {}
24
24
  }
@@ -22,10 +22,14 @@ Object.defineProperty(exports, '__esModule', { value: true });
22
22
  const setupIframe = (iframe, origin) => {
23
23
  if (typeof window === 'undefined')
24
24
  return () => { };
25
- const messageHandler = createMessageHandler(window, origin);
25
+ const messageHandler = createMessageHandler(window, origin, iframe);
26
26
  const messageSender = createMessageSender(iframe);
27
27
  // Add event listener to handle deeplink
28
28
  const setupOpenUrlHandler = () => messageHandler('OPEN_URL', ({ url, target = 'self', features }) => {
29
+ // This navigates the host top window, so never honor a URL whose scheme
30
+ // can execute script in the host origin (iframe->host XSS).
31
+ if (hasDangerousScheme(url))
32
+ return;
29
33
  if (target === 'blank') {
30
34
  window.open(url, '_blank', features);
31
35
  }
@@ -82,6 +86,41 @@ const setupIframe = (iframe, origin) => {
82
86
  return () => cleanupHandlers.forEach((offHandler) => offHandler());
83
87
  };
84
88
  // Utils
89
+ /**
90
+ * URL schemes that can execute script in the host origin. Navigating the host
91
+ * top window to any of these turns an OPEN_URL message into arbitrary code
92
+ * execution in the integrator's origin, so they are blocked.
93
+ *
94
+ * A deny-list (rather than an http/https allow-list) is used deliberately:
95
+ * wallet flows legitimately open custom-scheme deep links, and allow-listing
96
+ * http(s) would break them.
97
+ */
98
+ const DANGEROUS_SCHEMES = [
99
+ 'javascript:',
100
+ 'data:',
101
+ 'blob:',
102
+ 'vbscript:',
103
+ 'file:',
104
+ ];
105
+ /**
106
+ * Leading characters the browser ignores when resolving a URL scheme: spaces,
107
+ * tabs, newlines and other C0 control chars (U+0000–U+0020). Stripping them
108
+ * defeats obfuscation such as "\tjavascript:alert(1)".
109
+ */
110
+ // eslint-disable-next-line no-control-regex
111
+ const LEADING_IGNORED_CHARS = /^[\u0000-\u0020]+/;
112
+ /**
113
+ * Returns true when the URL begins with a script-executing scheme.
114
+ *
115
+ * Non-string input is treated as dangerous, leading ignored chars are stripped
116
+ * first, and the comparison is case-insensitive ("JavaScript:" is blocked).
117
+ */
118
+ const hasDangerousScheme = (url) => {
119
+ if (typeof url !== 'string')
120
+ return true;
121
+ const normalized = url.replace(LEADING_IGNORED_CHARS, '').toLowerCase();
122
+ return DANGEROUS_SCHEMES.some((scheme) => normalized.startsWith(scheme));
123
+ };
85
124
  /**
86
125
  * Custom event dispatched when the parent URL changes via the History API.
87
126
  * Used instead of re-patching history per setupIframe call so that multiple
@@ -117,10 +156,14 @@ const patchHistoryOnce = () => {
117
156
  /**
118
157
  * Create a message handler for the iframe
119
158
  */
120
- const createMessageHandler = (window, origin) => (eventName, handler) => {
159
+ const createMessageHandler = (window, origin, iframe) => (eventName, handler) => {
121
160
  const messageHandler = (event) => {
122
161
  if (event.origin !== origin)
123
162
  return;
163
+ // Only trust messages coming from this iframe's own window. Checking the
164
+ // origin alone lets any frame/tab at the SDK-iframe origin forge events.
165
+ if (event.source !== iframe.contentWindow)
166
+ return;
124
167
  const { eventName: incomingEventName, args } = event.data;
125
168
  if (eventName === incomingEventName) {
126
169
  handler(...args);
@@ -18,10 +18,14 @@
18
18
  const setupIframe = (iframe, origin) => {
19
19
  if (typeof window === 'undefined')
20
20
  return () => { };
21
- const messageHandler = createMessageHandler(window, origin);
21
+ const messageHandler = createMessageHandler(window, origin, iframe);
22
22
  const messageSender = createMessageSender(iframe);
23
23
  // Add event listener to handle deeplink
24
24
  const setupOpenUrlHandler = () => messageHandler('OPEN_URL', ({ url, target = 'self', features }) => {
25
+ // This navigates the host top window, so never honor a URL whose scheme
26
+ // can execute script in the host origin (iframe->host XSS).
27
+ if (hasDangerousScheme(url))
28
+ return;
25
29
  if (target === 'blank') {
26
30
  window.open(url, '_blank', features);
27
31
  }
@@ -78,6 +82,41 @@ const setupIframe = (iframe, origin) => {
78
82
  return () => cleanupHandlers.forEach((offHandler) => offHandler());
79
83
  };
80
84
  // Utils
85
+ /**
86
+ * URL schemes that can execute script in the host origin. Navigating the host
87
+ * top window to any of these turns an OPEN_URL message into arbitrary code
88
+ * execution in the integrator's origin, so they are blocked.
89
+ *
90
+ * A deny-list (rather than an http/https allow-list) is used deliberately:
91
+ * wallet flows legitimately open custom-scheme deep links, and allow-listing
92
+ * http(s) would break them.
93
+ */
94
+ const DANGEROUS_SCHEMES = [
95
+ 'javascript:',
96
+ 'data:',
97
+ 'blob:',
98
+ 'vbscript:',
99
+ 'file:',
100
+ ];
101
+ /**
102
+ * Leading characters the browser ignores when resolving a URL scheme: spaces,
103
+ * tabs, newlines and other C0 control chars (U+0000–U+0020). Stripping them
104
+ * defeats obfuscation such as "\tjavascript:alert(1)".
105
+ */
106
+ // eslint-disable-next-line no-control-regex
107
+ const LEADING_IGNORED_CHARS = /^[\u0000-\u0020]+/;
108
+ /**
109
+ * Returns true when the URL begins with a script-executing scheme.
110
+ *
111
+ * Non-string input is treated as dangerous, leading ignored chars are stripped
112
+ * first, and the comparison is case-insensitive ("JavaScript:" is blocked).
113
+ */
114
+ const hasDangerousScheme = (url) => {
115
+ if (typeof url !== 'string')
116
+ return true;
117
+ const normalized = url.replace(LEADING_IGNORED_CHARS, '').toLowerCase();
118
+ return DANGEROUS_SCHEMES.some((scheme) => normalized.startsWith(scheme));
119
+ };
81
120
  /**
82
121
  * Custom event dispatched when the parent URL changes via the History API.
83
122
  * Used instead of re-patching history per setupIframe call so that multiple
@@ -113,10 +152,14 @@ const patchHistoryOnce = () => {
113
152
  /**
114
153
  * Create a message handler for the iframe
115
154
  */
116
- const createMessageHandler = (window, origin) => (eventName, handler) => {
155
+ const createMessageHandler = (window, origin, iframe) => (eventName, handler) => {
117
156
  const messageHandler = (event) => {
118
157
  if (event.origin !== origin)
119
158
  return;
159
+ // Only trust messages coming from this iframe's own window. Checking the
160
+ // origin alone lets any frame/tab at the SDK-iframe origin forge events.
161
+ if (event.source !== iframe.contentWindow)
162
+ return;
120
163
  const { eventName: incomingEventName, args } = event.data;
121
164
  if (eventName === incomingEventName) {
122
165
  handler(...args);