CVE-2022-24762 (Markdown, published 2022-03-14T23:15Z)
Description: 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.
Severity: MEDIUM (CVSS v3 base score: 6.5)
Weakness: CWE-346 \u2014 Origin Validation Error
The product does not properly verify that the source of data or communication is valid.
Repository: https://github.com/jcubic/sysend.js
Files changed:
README.md
package.json
proxy.html
sysend.d.ts
sysend.js
Commit message: fix security issue with Cross-Domain communication #33


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() {