Allowing guests to cooperate

Host pages can arrange for guests to communicate with one another. Here, we show a simple example.

Create guest code

For this example, we'll create two guests. The first (also here) is as follows:

<html>
  <head></head>
  <body>
    <input id="a" value="Click to get the value">
    <button onclick="document.getElementById('a').value = get();">Get</button>
  </body>
</html>

which looks like this when rendered:

The second (also here) is:

<html>
  <head></head>
  <body>
    <input id="a">
    <button onclick="set(document.getElementById('a').value);">Set</button>
  </body>
</html>

and looks like this when rendered:

The first guest expects a function called get which it calls to obtain some value when the user presses the button. The second expects a function called set which it uses to assign some value when the user presses the button. In this tutorial, will put them in touch with each other, so that they can communicate the value back and forth, but are otherwise protected from each other, and the host page is protected from them.

Create host page

The host page we create for these guests is as follows (and is also available here):

<html>
  <head>
    <title>Caja host page</title>
    <script type="text/javascript"
            src="//caja.appspot.com/caja.js">
    </script>
  </head>

  <body>
    <h1>Caja host page</h1>
    <div id="guest-get" style="height: 100px;"></div>
    <div id="guest-set" style="height: 100px;"></div>
    <script type="text/javascript">
      var shared = (function (x) {  // (1)
        return {
          get: function () { return x; },
          set: function (x_) { x = '' + x_; }  // (2)
        };
      })("");

      caja.initialize({
        cajaServer: 'https://caja.appspot.com/',
        debug: true
      });
      caja.load(document.getElementById('guest-get'), undefined, function(frame) {
        var tamedGet = caja.tame(caja.markFunction(shared.get));
        frame.code('https://developers.google.com/caja/demos/allowingcooperation/guest-get.html',
                   'text/html')
             .api({ get: tamedGet })  // (3)
             .run();
      });
      caja.load(document.getElementById('guest-set'), undefined, function(frame) {
        var tamedSet = caja.tame(caja.markFunction(shared.set));

        frame.code('https://developers.google.com/caja/demos/allowingcooperation/guest-set.html',
                   'text/html')
             .api({ set: tamedSet })  // (4)
             .run();
      });
    </script>
  </body>
</html>

(1) We create a "cell" object that exposes two functions, get and set, allowing clients to get and set its value, respectively. As an example of the defensive programming needed when dealing with untrusted third-party guests, note how we hide the actual variable x from the code around it, reducing the likelihood that our host page will accidentally leak the authority to modify it in an unexpected way. Note also how, at (2), we only store the immutable string representation of the incoming value, to ensure that the two guests cannot communicate more complex objects to one another, and so simplify our reasoning about the effects of the system we construct.

At (3) and (4), respectively, we give the guests the authority to get and set the shared variable.

The result of rendering the host page should look like this:

Run it yourself

Review

  • You created an API allowing two guests to communicate in a controlled fashion; and
  • You learned more about the design reasoning required for secure communication with, and between, untrusted third-party guests.

Next steps