CVE-2022-24762: Origin Validation Error (CWE-346)
value: sysend.js is a library that allows a user to send messages between pages that are open in the same browser. Users that use cross-origin communication may have their communications intercepted. Impact is limited by the communication occurring in the same browser. This issue has been patched in sysend.js version 1.10.0. The only currently known workaround is to avoid sending communications that a user does not want to have intercepted via sysend messages.
CWE description: The product does not properly verify that the source of data or communication is valid.
CVSS v3 base score: 6.5
Published: 2022-03-14T23:15Z
Repository: https://github.com/jcubic/sysend.js
Language: Markdown
Files changed: README.md, package.json, proxy.html, sysend.d.ts, sysend.js
Commit date: 2022-03-13 14:21:22 +0100
Commit message: fix security issue with Cross-Domain communication #33

Diff: diff --git a/README.md b/README.md
index caef719..5dbb2a2 100644
--- a/README.md
+++ b/README.md
@@ -1,38 +1,37 @@
 <p align="center">
   <img src="https://github.com/jcubic/sysend.js/blob/master/assets/logo.svg?raw=true" alt="Sysend.js logo"/>
 </p>
 
-[![npm](https://img.shields.io/badge/npm-1.9.0-blue.svg)](https://www.npmjs.com/package/sysend)
-![bower](https://img.shields.io/badge/bower-1.9.0-yellow.svg)
+[![npm](https://img.shields.io/badge/npm-1.10.0-blue.svg)](https://www.npmjs.com/package/sysend)
+![bower](https://img.shields.io/badge/bower-1.10.0-yellow.svg)
 ![downloads](https://img.shields.io/npm/dt/sysend.svg)
 [![jsdelivr](https://img.shields.io/jsdelivr/npm/hm/sysend)](https://www.jsdelivr.com/package/npm/sysend)
 
 # [Web application synchronization between different tabs](https://github.com/jcubic/sysend.js/)
 
-sysend.js is a small library that allows to send messages between pages that are
-open in the same browser. It also supports Cross-Domain communication. The library doesn't have
-any dependencies and uses the HTML5 LocalStorage API or BroadcastChannel API.
-If your browser don't support BroadcastChannel (see [Can I Use](https://caniuse.com/#feat=broadcastchannel))
-then you can send any object that can be serialized to JSON. With BroadcastChannel you can send any object
-(it will not be serialized to string but the values are limited to the ones that can be copied by
-the [structured cloning algorithm](https://html.spec.whatwg.org/multipage/structured-data.html#structured-clone)).
+sysend.js is a small library that allows to send messages between pages that are open in the same
+browser. It also supports Cross-Domain communication (Cross-Origin). The library doesn't have any
+dependencies and uses the HTML5 LocalStorage API or BroadcastChannel API.  If your browser don't
+support BroadcastChannel (see [Can I Use](https://caniuse.com/#feat=broadcastchannel)) then you can
+send any object that can be serialized to JSON. With BroadcastChannel you can send any object (it
+will not be serialized to string but the values are limited to the ones that can be copied by the
+[structured cloning algorithm](https://html.spec.whatwg.org/multipage/structured-data.html#structured-clone)).
 You can also send empty notifications.
 
 Tested on:
 
 GNU/Linux: in Chromium 34, FireFox 29, Opera 12.16 (64bit)<br/>
 Windows 10 64bit: in IE11 and Edge 38, Chrome 56, Firefox 51<br/>
 MacOS X El Captain: Safari 9, Chrome 56, Firefox 51
 
 ## Note about Safari 7+ and Cross-Domain communication
 
-All cross-domain communication is disabled by default with Safari 7+.
-Because of a feature that block 3rd party tracking for iframe, and any
-iframe used for cross-domain communication runs in sandboxed environment.
-That's why this library like any other solution for cross-domain comunication,
-don't work on Safari.
+All cross-domain communication is disabled by default with Safari 7+.  Because of a feature that
+block 3rd party tracking for iframe, and any iframe used for cross-domain communication runs in
+sandboxed environment.  That's why this library like any other solution for cross-domain
+comunication, don't work on Safari.
 
 ## Installation
 
 Include `sysend.js` file in your html, you can grab the file from npm:
 
@@ -104,27 +103,10 @@ Here is another example that shows [ReactJS shopping cart synchronization](https
 
 And here is [multiple window tracking demo](https://jcubic.pl/windows.html). Open the link in multiple windows (not tabs). First window will track position and size for all windows.
 
 ![Screen capture of Operating System Windows dragging and moving around animation](https://github.com/jcubic/sysend.js/blob/master/assets/windows-demo.gif?raw=true)
 
-## Cross-domain Commuication Security
-
-The iframe communication proxy, allow attackers to listen to any events sent to the iframe.
-All they need to do is to use this code on any domain to connect to sysend channel:
-
-```javascript
-sysend.proxy('https://jcubic.pl/');
-window.addEventListener('message', (e) => {
-    console.log(e);
-});
-```
-
-This can lead to potential leaking of sensitive information from the website.
-As of now there are not soution how to secure sysend cross-domain communication channel.
-
-**To protect your application, don't send any sensitive infromation with cross-domain communication!**
-
 ## API
 
 sysend object:
 
 | function | description | arguments | Version |
@@ -135,13 +117,15 @@ sysend object:
 | `proxy(url)` | create iframe proxy for different domain, the target domain/URL should have [proxy.html](https://github.com/jcubic/sysend.js/blob/master/proxy.html)<br> file. If url domain is the same as page domain, it's ignored. So you can put both proxy calls on both | url - string | 1.3.0 |
 | `serializer(to_string, from_string)` | add serializer and deserializer functions | both arguments are functions (data: any) => string | 1.4.0 |
 | `emit(name, [, object])` | same as `broadcast()` but also invoke the even on same page | name - string - The name of the event<br>object - optional any data | 1.5.0 |
 | `post(<window_id>, [, object])` | send any data to other window | window_id - string of the target window<br>object - any data | 1.6.0 |
 | `list()` | returns a Promise of objects `{id:<UUID>, primary}` for other windows, you can use those to send a message with `post()` | NA | 1.6.0 |
-| `track(event, callback)` | track inter window communication events  | event - any of the strings: `"open"`, `"close"`, `"primary"`, <br>`"secondary"`, `"message"`<br>callback - different function depend on the event:<br>* `"message"` - `{data, origin}` - where data is anything the `post()` sends, and origin is `id` of the sender.<br>* `"open"` - `{count, primary, id}` when new window/tab is opened<br>* `"close"` - `{count, primary, id, self}` when window/tab is closed<br>* `"primary"` and `"secondary"` function has no arguments and is called when window/tab become secondary or primary.<br>* `"ready"` - event when tracking is ready. | 1.6.0 except `ready` - 1.9.0 |
+| `track(event, callback)` | track inter window communication events  | event - any of the strings: `"open"`, `"close"`, `"primary"`, <br>`"secondary"`, `"message"`<br>callback - different function depend on the event:<br>* `"message"` - `{data, origin}` - where data is anything the `post()` sends, and origin is `id` of the sender.<br>* `"open"` - `{count, primary, id}` when new window/tab is opened<br>* `"close"` - `{count, primary, id, self}` when window/tab is closed<br>* `"primary"` and `"secondary"` function has no arguments and is called when window/tab become secondary or primary.<br>* `"ready"` - event when tracking is ready. | 1.6.0 except `ready` - 1.10.0 |
 | `untrack(event [,callback])` | remove sigle event listener all all listeners for a given event | event - any of the strings `'open'`, `'close'`, `'primary'`, `'secondary'`, or `'message'`. | 1.6.0 |
 | `isPrimary()` | function returns true if window is primary (first open or last that remain) | NA  | 1.6.0 |
+| `channel()` | function restrict cross domain communication to only allowed domains. You need to call this function on proxy iframe to limit number of domains (origins) that can listen and send events.  | any number of origins (e.g. 'http://localhost:8080' or 'https://jcubic.github.io') you can also use valid URL. | 1.10.0 |
+
 
 To see details of using the API, see [demo.html source code](https://github.com/jcubic/sysend.js/blob/master/demo.html) or [TypeScript definition file](https://github.com/jcubic/sysend.js/blob/master/sysend.d.ts).
 
 ## License
 
diff --git a/package.json b/package.json
index 8b55941..5a81741 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
   "name": "sysend",
-  "version": "1.9.0",
+  "version": "1.10.0",
   "description": "Web application synchronization between different tabs",
   "main": "sysend.js",
   "typings": "sysend.d.ts",
   "scripts": {
   },
diff --git a/proxy.html b/proxy.html
index be8581c..907bb54 100644
--- a/proxy.html
+++ b/proxy.html
@@ -1,10 +1,14 @@
 <!DOCTYPE HTML>
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
     <meta charset="utf-8" />
     <meta name="robots" content="noindex,nofollow"/>
-    <script src="https://cdn.jsdelivr.net/npm/sysend"></script>
+    <script src="./sysend.js"></script>
+    <!-- EDIT THIS FILE AND CALL sysend.channel() WITH ALLOWED DOMAINS -->
+    <script>
+       sysend.channel();
+    </script>
 </head>
 <body>
 </body>
 </html>
diff --git a/sysend.d.ts b/sysend.d.ts
index 97b12b2..a369b30 100644
--- a/sysend.d.ts
+++ b/sysend.d.ts
@@ -1,7 +1,7 @@
 /**@license
- *  sysend.js - send messages between browser windows/tabs version 1.9.0
+ *  sysend.js - send messages between browser windows/tabs version 1.10.0
  *
  *  Copyright (C) 2014-2022 Jakub T. Jankiewicz <https://jcubic.pl/me>
  *  Released under the MIT license
  *
  */
@@ -21,10 +21,11 @@ interface Sysend {
     track(event: 'message', callback: (payload: {data: any, origin: string}) => void): void;
     track(event: 'secondary', callback: () => void): void;
     untrack(event: 'open' | 'close' | 'primary' | 'secondary' | 'message', fn?: (input?: any) => void): void;
     list(): Promise<Array<{ id: string, primary: boolean }>>;
     post(target: string, data?: any): void;
+    channel(...domains: string[]): void;
     isPrimary(): boolean;
 }
 
 declare const sysend: Sysend;
 
diff --git a/sysend.js b/sysend.js
index aaa25e5..3ba5334 100644
--- a/sysend.js
+++ b/sysend.js
@@ -1,7 +1,7 @@
 /**@license
- *  sysend.js - send messages between browser windows/tabs version 1.9.0
+ *  sysend.js - send messages between browser windows/tabs version 1.10.0
  *
  *  Copyright (C) 2014-2022 Jakub T. Jankiewicz <https://jcubic.pl/me>
  *  Released under the MIT license
  *
  *  The idea for localStorage implementation came from this StackOverflow question:
@@ -39,10 +39,11 @@
     var list_id = 0;
 
     // id of the window/tab
     var target_id = generate_uuid();
     var target_count = 1;
+    var domains;
 
     var handlers = {
         primary: [],
         close: [],
         open: [],
@@ -82,10 +83,12 @@
             serializer.to = to;
             serializer.from = from;
         },
         proxy: function(url) {
             if (typeof url === 'string' && host(url) !== window.location.host) {
+                domains = domains || [];
+                domains.push(origin(url));
                 var iframe = document.createElement('iframe');
                 iframe.style.width = iframe.style.height = 0;
                 iframe.style.border = 'none';
                 var proxy_url = url;
                 if (!url.match(/\.html$/)) {
@@ -174,10 +177,13 @@
                 timer().then(function() {
                     resolve(ids);
                 });
             });
         },
+        channel: function() {
+            domains = [].slice.apply(arguments).map(origin);
+        },
         isPrimary: function() {
             return primary;
         }
     };
     // -------------------------------------------------------------------------
@@ -205,27 +211,49 @@
             a.href = url;
             return a.host;
         };
     })();
     // -------------------------------------------------------------------------
-    init();
-    // -------------------------------------------------------------------------
     function delay(time) {
         return function() {
             return new Promise(function(resolve) {
                 setTimeout(resolve, time);
             });
         };
     }
     // -------------------------------------------------------------------------
-    function onLoad() {
+    var origin = (function() {
+        var a = document.createElement('a');
+        return function origin(url) {
+            a.href = url;
+            return a.origin;
+        };
+    })();
+    // -------------------------------------------------------------------------
+    // :: show only single message of this kind
+    // -------------------------------------------------------------------------
+    var warn_messages = [];
+    function warn(message) {
+        if (!warn_messages.includes(message)) {
+            warn_messages.push(message);
+            if (console && console.warn) {
+                console.warn(message);
+            } else {
+                setTimeout(function() {
+                    throw new Error(message);
+                }, 0);
+            }
+        }
+    }
+    // -------------------------------------------------------------------------
+    function on_load() {
         return new Promise(function(resolve) {
             window.addEventListener('load', resolve, true);
-        }).then(iframeLoaded);
+        }).then(iframe_loaded);
     }
     // -------------------------------------------------------------------------
-    function iframeLoaded() {
+    function iframe_loaded() {
         var iframes = Array.from(document.querySelectorAll('iframe'));
         return Promise.all(iframes.filter(function(iframe) {
             return iframe.src;
         }).map(function(iframe) {
             return new Promise(function(resolve, reject) {
@@ -237,12 +265,34 @@
         })).then(delay(sysend.timeout));
         // delay is required, something with browser is not intitled properly
         // the number was pick by experimentation
     }
     // -------------------------------------------------------------------------
-    // ref: https://stackoverflow.com/a/8809472/387194
-    // license: Public Domain/MIT
+    // :: valid sysend message
+    // -------------------------------------------------------------------------
+    function is_sysend_post_message(e) {
+        return typeof e.data === 'string' && e.data.match(prefix_re);
+    }
+    // -------------------------------------------------------------------------
+    function is_valid_origin(origin) {
+        if (!domains) {
+            warn('Call sysend.channel() on iframe to restrict domains that can '+
+                 'use sysend channel');
+            return true;
+        }
+        var valid = domains.includes(origin);
+        if (!valid) {
+            warn(origin + ' domain is not on the list of allowed '+
+                 'domains use sysend.channel() on iframe to allow'+
+                 ' access to this domain');
+        }
+        return valid;
+    }
+    // -------------------------------------------------------------------------
+    // :: ref: https://stackoverflow.com/a/8809472/387194
+    // :: license: Public Domain/MIT
+    // -------------------------------------------------------------------------
     function generate_uuid() {
         var d = new Date().getTime();
         //Time in microseconds since page-load or 0 if unsupported
         var d2 = (performance && performance.now && (performance.now() * 1000)) || 0;
         return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
@@ -293,34 +343,36 @@
                 if (fn) {
                     return fn(data);
                 }
                 return data;
             } catch (e) {
-                console.warn(prefix_message + e.message);
+                warn(prefix_message + e.message);
             }
         };
     }
     // -------------------------------------------------------------------------
     // ref: https://stackoverflow.com/a/326076/387194
     // -------------------------------------------------------------------------
-    function is_iframe() {
+    var is_iframe = (function is_iframe() {
         try {
             return window.self !== window.top;
         } catch (e) {
             return true;
         }
-    }
+    })();
     // -------------------------------------------------------------------------
     function send_to_iframes(key, data) {
         // propagate events to iframes
         iframes.forEach(function(iframe) {
             var payload = {
               name: uniq_prefix,
               key: key,
               data: data
             };
-            iframe.window.postMessage(JSON.stringify(payload), "*");
+            if (is_valid_origin(origin(iframe.node.src))) {
+                iframe.window.postMessage(JSON.stringify(payload), "*");
+            }
         });
     }
     // -------------------------------------------------------------------------
     function to_json(input) {
         // save random_value in storage to fix issue in IE that storage event
@@ -385,25 +437,38 @@
         primary = true;
         trigger(handlers.primary);
         sysend.emit('__primary__');
     }
     // -------------------------------------------------------------------------
+    init();
+    // -------------------------------------------------------------------------
     function init() {
         if (typeof window.BroadcastChannel === 'function') {
             channel = new window.BroadcastChannel(uniq_prefix);
             channel.addEventListener('message', function(event) {
                 if (event.target.name === uniq_prefix) {
-                    var key = event.data && event.data.name;
-                    if (callbacks[key]) {
-                        invoke(key, unserialize(event.data.data));
+                    if (is_iframe) {
+                        var payload = {
+                          name: uniq_prefix,
+                          data: event.data,
+                          iframe_id: target_id
+                        };
+                        if (is_valid_origin(origin(document.referrer))) {
+                            window.parent.postMessage(JSON.stringify(payload), "*");
+                        }
+                    } else {
+                        var key = event.data && event.data.name;
+                        if (callbacks[key]) {
+                            invoke(key, unserialize(event.data.data));
+                        }
                     }
                 }
             });
         } else if (is_private_mode()) {
-            console.warn('Your browser don\'t support localStorgage. ' +
-                         'In Safari this is most of the time because ' +
-                         'of "Private Browsing Mode"');
+            warn('Your browser don\'t support localStorgage. ' +
+                 'In Safari this is most of the time because ' +
+                 'of "Private Browsing Mode"');
         } else {
             // we need to clean up localStorage if broadcast called on unload
             // because setTimeout will never fire, even setTimeout 0
             var re = new RegExp('^' + uniq_prefix);
             for(var key in localStorage) {
@@ -427,13 +492,13 @@
                     }
                 }
             }, false);
         }
 
-        if (is_iframe()) {
+        if (is_iframe) {
             window.addEventListener('message', function(e) {
-                if (typeof e.data === 'string' && e.data.match(prefix_re)) {
+                if (is_sysend_post_message(e) && is_valid_origin(e.origin)) {
                     try {
                         var payload = JSON.parse(e.data);
                         if (payload && payload.name === uniq_prefix) {
                             var data = unserialize(payload.data);
                             sysend.broadcast(payload.key, data);
@@ -446,11 +511,10 @@
             });
         } else {
             init_visiblity();
 
             sysend.track('visbility', function(visible) {
-                console.log({visible, has_primary});
                 if (visible && !has_primary) {
                     become_primary();
                 }
             });
 
@@ -516,11 +580,11 @@
 
             addEventListener('beforeunload', function() {
                 sysend.emit('__close__', { id: target_id, wasPrimary: primary });
             }, { capture: true });
 
-            onLoad().then(function() {
+            on_load().then(function() {
                 sysend.list().then(function(list) {
                     target_count = list.length;
                     primary = list.length === 0;
                     var found = list.find(function(item) {
                         return item.primary;

Vulnerable code: [![npm](https://img.shields.io/badge/npm-1.9.0-blue.svg)](https://www.npmjs.com/package/sysend)
![bower](https://img.shields.io/badge/bower-1.9.0-yellow.svg)
sysend.js is a small library that allows to send messages between pages that are
open in the same browser. It also supports Cross-Domain communication. The library doesn't have
any dependencies and uses the HTML5 LocalStorage API or BroadcastChannel API.
If your browser don't support BroadcastChannel (see [Can I Use](https://caniuse.com/#feat=broadcastchannel))
then you can send any object that can be serialized to JSON. With BroadcastChannel you can send any object
(it will not be serialized to string but the values are limited to the ones that can be copied by
the [structured cloning algorithm](https://html.spec.whatwg.org/multipage/structured-data.html#structured-clone)).
All cross-domain communication is disabled by default with Safari 7+.
Because of a feature that block 3rd party tracking for iframe, and any
iframe used for cross-domain communication runs in sandboxed environment.
That's why this library like any other solution for cross-domain comunication,
don't work on Safari.
## Cross-domain Commuication Security

The iframe communication proxy, allow attackers to listen to any events sent to the iframe.
All they need to do is to use this code on any domain to connect to sysend channel:

```javascript
sysend.proxy('https://jcubic.pl/');
window.addEventListener('message', (e) => {
    console.log(e);
});
```

This can lead to potential leaking of sensitive information from the website.
As of now there are not soution how to secure sysend cross-domain communication channel.

**To protect your application, don't send any sensitive infromation with cross-domain communication!**

| `track(event, callback)` | track inter window communication events  | event - any of the strings: `"open"`, `"close"`, `"primary"`, <br>`"secondary"`, `"message"`<br>callback - different function depend on the event:<br>* `"message"` - `{data, origin}` - where data is anything the `post()` sends, and origin is `id` of the sender.<br>* `"open"` - `{count, primary, id}` when new window/tab is opened<br>* `"close"` - `{count, primary, id, self}` when window/tab is closed<br>* `"primary"` and `"secondary"` function has no arguments and is called when window/tab become secondary or primary.<br>* `"ready"` - event when tracking is ready. | 1.6.0 except `ready` - 1.9.0 |
  "version": "1.9.0",
    <script src="https://cdn.jsdelivr.net/npm/sysend"></script>
 *  sysend.js - send messages between browser windows/tabs version 1.9.0
 *  sysend.js - send messages between browser windows/tabs version 1.9.0
    init();
    // -------------------------------------------------------------------------
    function onLoad() {
        }).then(iframeLoaded);
    function iframeLoaded() {
    // ref: https://stackoverflow.com/a/8809472/387194
    // license: Public Domain/MIT
                console.warn(prefix_message + e.message);
    function is_iframe() {
    }
            iframe.window.postMessage(JSON.stringify(payload), "*");
                    var key = event.data && event.data.name;
                    if (callbacks[key]) {
                        invoke(key, unserialize(event.data.data));
            console.warn('Your browser don\'t support localStorgage. ' +
                         'In Safari this is most of the time because ' +
                         'of "Private Browsing Mode"');
        if (is_iframe()) {
                if (typeof e.data === 'string' && e.data.match(prefix_re)) {
                console.log({visible, has_primary});
            onLoad().then(function() {
Fixed code: [![npm](https://img.shields.io/badge/npm-1.10.0-blue.svg)](https://www.npmjs.com/package/sysend)
![bower](https://img.shields.io/badge/bower-1.10.0-yellow.svg)
sysend.js is a small library that allows to send messages between pages that are open in the same
browser. It also supports Cross-Domain communication (Cross-Origin). The library doesn't have any
dependencies and uses the HTML5 LocalStorage API or BroadcastChannel API.  If your browser don't
support BroadcastChannel (see [Can I Use](https://caniuse.com/#feat=broadcastchannel)) then you can
send any object that can be serialized to JSON. With BroadcastChannel you can send any object (it
will not be serialized to string but the values are limited to the ones that can be copied by the
[structured cloning algorithm](https://html.spec.whatwg.org/multipage/structured-data.html#structured-clone)).
All cross-domain communication is disabled by default with Safari 7+.  Because of a feature that
block 3rd party tracking for iframe, and any iframe used for cross-domain communication runs in
sandboxed environment.  That's why this library like any other solution for cross-domain
comunication, don't work on Safari.
| `track(event, callback)` | track inter window communication events  | event - any of the strings: `"open"`, `"close"`, `"primary"`, <br>`"secondary"`, `"message"`<br>callback - different function depend on the event:<br>* `"message"` - `{data, origin}` - where data is anything the `post()` sends, and origin is `id` of the sender.<br>* `"open"` - `{count, primary, id}` when new window/tab is opened<br>* `"close"` - `{count, primary, id, self}` when window/tab is closed<br>* `"primary"` and `"secondary"` function has no arguments and is called when window/tab become secondary or primary.<br>* `"ready"` - event when tracking is ready. | 1.6.0 except `ready` - 1.10.0 |
| `channel()` | function restrict cross domain communication to only allowed domains. You need to call this function on proxy iframe to limit number of domains (origins) that can listen and send events.  | any number of origins (e.g. 'http://localhost:8080' or 'https://jcubic.github.io') you can also use valid URL. | 1.10.0 |

  "version": "1.10.0",
    <script src="./sysend.js"></script>
    <!-- EDIT THIS FILE AND CALL sysend.channel() WITH ALLOWED DOMAINS -->
    <script>
       sysend.channel();
    </script>
 *  sysend.js - send messages between browser windows/tabs version 1.10.0
    channel(...domains: string[]): void;
 *  sysend.js - send messages between browser windows/tabs version 1.10.0
    var domains;
                domains = domains || [];
                domains.push(origin(url));
        channel: function() {
            domains = [].slice.apply(arguments).map(origin);
        },
    var origin = (function() {
        var a = document.createElement('a');
        return function origin(url) {
            a.href = url;
            return a.origin;
        };
    })();
    // -------------------------------------------------------------------------
    // :: show only single message of this kind
    // -------------------------------------------------------------------------
    var warn_messages = [];
    function warn(message) {
        if (!warn_messages.includes(message)) {
            warn_messages.push(message);
            if (console && console.warn) {
                console.warn(message);
            } else {
                setTimeout(function() {
                    throw new Error(message);
                }, 0);
            }
        }
    }
    // -------------------------------------------------------------------------
    function on_load() {
        }).then(iframe_loaded);
    function iframe_loaded() {
    // :: valid sysend message
    // -------------------------------------------------------------------------
    function is_sysend_post_message(e) {
        return typeof e.data === 'string' && e.data.match(prefix_re);
    }
    // -------------------------------------------------------------------------
    function is_valid_origin(origin) {
        if (!domains) {
            warn('Call sysend.channel() on iframe to restrict domains that can '+
                 'use sysend channel');
            return true;
        }
        var valid = domains.includes(origin);
        if (!valid) {
            warn(origin + ' domain is not on the list of allowed '+
                 'domains use sysend.channel() on iframe to allow'+
                 ' access to this domain');
        }
        return valid;
    }
    // -------------------------------------------------------------------------
    // :: ref: https://stackoverflow.com/a/8809472/387194
    // :: license: Public Domain/MIT
    // -------------------------------------------------------------------------
                warn(prefix_message + e.message);
    var is_iframe = (function is_iframe() {
    })();
            if (is_valid_origin(origin(iframe.node.src))) {
                iframe.window.postMessage(JSON.stringify(payload), "*");
            }
    init();
    // -------------------------------------------------------------------------
                    if (is_iframe) {
                        var payload = {
                          name: uniq_prefix,
                          data: event.data,
                          iframe_id: target_id
                        };
                        if (is_valid_origin(origin(document.referrer))) {
                            window.parent.postMessage(JSON.stringify(payload), "*");
                        }
                    } else {
                        var key = event.data && event.data.name;
                        if (callbacks[key]) {
                            invoke(key, unserialize(event.data.data));
                        }
            warn('Your browser don\'t support localStorgage. ' +
                 'In Safari this is most of the time because ' +
                 'of "Private Browsing Mode"');
        if (is_iframe) {
                if (is_sysend_post_message(e) && is_valid_origin(e.origin)) {
            on_load().then(function() {