• Stars
    star
    281
  • Rank 143,976 (Top 3 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 12 years ago
  • Updated over 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A lightweight library for embedding untrusted content and exposing capabilities. I guess it does two things? But it does them well.

Oasis.js

Build Status

Oasis.js is a pleasant API for safe communication with untrusted code in sandboxed iframes.

For example, imagine we are using a third-party profile viewer to display information about a user. We only want to expose the bare minimum required to use the widget, without giving it access to all of the parent's environment.

Here is what your application would look like:

<!doctype html>

<html>
  <head>
    <script src="http://example.com/oasis.js.html"></script>
  </head>
  <body>
    <script>
      var sandbox = oasis.createSandbox({
        url: 'http://example.com/profile_viewer.html',
        type: 'html',
        capabilities: [ 'account' ]
      });

      sandbox.connect('account').then(function(port) {
        port.onRequest('profile', function () {
          return { email: '[email protected]' };
        });
      });

      document.body.appendChild(sandbox.el);
    </script>
  </body>
</html>

And here is the profile viewer widget (hosted either on your domain or a third-party's domain):

<!doctype html>

<html>
  <head>
    <script src="http://example.com/jquery.js"></script>
    <script src="http://example.com/oasis.js.html"></script>
  </head>
  <body>
    <div>
      <p>Email: <span id="email"><img src="loading.png"></span></p>
    </div>
    <script>
      oasis.connect('account').then(function(port) {
        port.request('profile').then(function(profile) {
          $("#email").html(profile.email);
        });
      });
    </script>
  </body>
</html>

API

Creating Sandboxes

Sandboxed applications or widgets can be hosted as JavaScript or HTML. Both can be sandboxed inside an iframe, but Oasis can also sandbox JavaScript widgets inside a web worker.

Sandboxes are created via the createSandbox API.

Here is an example of creating an iframe sandbox for a JavaScript widget:

oasis.createSandbox({
  url: 'http://example.com/profile_viewer.js',
  capabilities: [ 'account' ]
});

When creating JavaScript sandboxes it is necessary to host Oasis on the same domain as the sandboxed JavaScript (see Browser Support).

Here is an example of creating an iframe sandbox for an HTML widget:

oasis.createSandbox({
  url: 'http://example.com/profile_viewer.html',
  type: 'html',
  capabilities: [ 'account' ]
});

When creating HTML sandboxes, it is the sandbox's responsibility to load Oasis (typically via a script tag in the head element).

Sandboxed widgets that require no UI can be loaded as web workers:

  url: 'http://example.com/profile_information.js',
  capabilities: [ 'account' ],
  adapter: oasis.adapters.webworker

The application can grant specific privileges to the sandbox, like opening windows.

oasis.createSandbox({
  url: 'http://example.com/profile_viewer.html',
  type: 'html',
  capabilities: [ 'account' ],
  sandbox: {
    popups: true
  }
});

Starting Sandboxes

Web worker sandboxes will start immediately. HTML (ie iframe) sandboxes will start as soon as their DOM element is placed in the document. The simplest way to do this is to append them to the body:

document.body.appendChild(sandbox.el);

But they can be placed anywhere in the DOM. Please note that once in the DOM the sandboxes should not be moved: iframes moved within documents are reloaded by the browser.

Connecting to Ports Directly

For simple applications it can be convenient to connect directly to ports for a provided capability.

When doing so, you can send messages via send. Messages can be sent in either direction.

  // in the environment
  sandbox.connect('account').then(function(port) {
    port.send('greeting', 'Hello World!')
  });

  // in the sandbox
  oasis.connect('account').then(function(port) {
    port.on('greeting', function (message) {
      document.body.innerHTML = '<strong>' + message + '</strong>';
    });
  });

You can also request data via request and respond to data via onRequest.

  // in the environment
  sandbox.connect('account').then(function(port) {
    port.onRequest('profile', function () {
      return { name: 'Yehuda Katz' };
    })
  });

  // in the sandbox
  oasis.connect('account').then(function(port) {
    port.request('profile').then( function (name) {
      document.body.innerHTML = 'Hello ' + name;
    });
  });

You can also respond to requests with promises, in case you need to retrieve the data asynchronously. This example uses rsvp, but any Promises/A+ implementation is supported.

  // in the environment
  sandbox.connect('account').then(function(port) {
    port.onRequest('profile', function () {
      return new Oasis.RSVP.Promise( function (resolve, reject) {
        setTimeout( function () {
          // Here we're using `setTimeout`, but a more realistic case would
          // involve XMLHttpRequest, IndexedDB, FileSystem &c.
          resolve({ name: 'Yehuda Katz' });
        }, 1);
      });
    })
  });

  // in the sandbox
  oasis.connect('account').then(function(port) {
    // the sandbox code remains unchanged
    port.request('profile').then( function (name) {
      document.body.innerHTML = 'Hello ' + name;
    });
  });

Using Services and Consumers

You can provide services for a sandbox's capabilities to take advantage of a shorthand for specifying events and request handlers.

  var AccountService = Oasis.Service.extend();
  var sandbox = oasis.createSandbox({
    url: 'http://example.com/profile_viewer.js',
    capabilities: [ 'account' ],
    services: {
      account: AccountService
    }
  });

This functionality is available within the sandbox as well: simply specify consumers when connecting, rather than connecting to each port individually.

var AccountConsumer = Oasis.Consumer.extend();
oasis.connect({
  consumers: {
    account: AccountConsumer
  }
})

Note that Oasis.Service and Oasis.Consumer are class-like, so we refer to them via Oasis. oasis, which we've been using for things like createSandbox, is an instance of Oasis created automatically. You normally only need this implicit instance, but it's possible to have multiple groups of sandboxes isolated from each other, although this is an advanced feature.

Services and Consumers can use an events shorthand for conveniently defining event handlers:

  var AccountService = Oasis.Service.extend({
    events: {
      updatedName: function(newName) {
        user.set('name', newName);
      }
    }
  });

They can also use a requests shorthand for easily defining request handlers.

  var UserService = Oasis.Service.extend({
    requests: {
      basicInformation: function(user) {
        switch (user) {
          case 'wycats':
            return { name: 'Yehuda Katz' };
          case 'hjdivad':
            return { name: 'David J. Hamilton' };
        }
      },

      // The `requests` shorthand also supports asynchronous responses via
      // promises.
      extraInformation: function(user) {
        return new Oasis.RSVP.Promise( function (resolve, reject) {
          // if `loadExtraInformationAsynchronously` returned a promise we could
          // return it directly, as with jQuery's `ajax`.
          loadExtraInformationAsynchronously( function(userInformation) {
            resolve(userInformation);
          });
        });
      }
    }
  });

Wiretapping Sandboxes

Sometimes it's helpful to listen to many, or even all, messages sent to or received from, a sandbox. This can be particularly useful in testing.

  sandbox.wiretap( function(capability, message) {
    console.log(capability, message.type, message.data, message.direction);
  });

Requirements & Browser Support

Oasis.js is designed to take advantage of current and upcoming features in modern browsers.

  • <iframe sandbox>: An HTML5 feature that allows strict sandboxing of content, even served on the same domain. Available in all Evergreen browsers and IE10+.
  • MessageChannel: An HTML5 feature that allows granular communication between iframes. It replaces the need to do cumbersome multiplexing over a single postMessage channel. Available in all Evergreen browsers (and IE10+) with the exception of Firefox.
  • postMessage structured data: An HTML5 feature that allows sending structured data, not just strings, over postMessage.

Oasis.js supports Chrome, Firefox, Safari 6, and Internet Explorer 8+. Support for older browsers depends on polyfills.

  • MessageChannel.js polyfills MessageChannel where it is unavailable (IE8, IE9 and Firefox).
  • Kamino.js polyfills postMessage structured data for Internet Explorer.

Support for IE8 and IE9 depends on the sandboxes being hosted on an origin that differs from the environment, as these versions of IE do not support <iframe sandbox>. Oasis.js will refuse to create a sandbox if the sandbox attribute is not supported and the domains are the same.

Building Oasis.js

Make sure you have node and grunt installed. Then, run:

npm install
grunt build

Testing Oasis.js

To run the Oasis.js test, run:

grunt server

Then navigate to http://localhost:8000

Samples

The easiest way to see the samples is to run the test server and navigate to http://localhost:8000/samples.

More Repositories

1

rsvp.js

A lightweight library that provides tools for organizing asynchronous code
JavaScript
3,619
star
2

helix

Native Ruby extensions without fear
Rust
1,980
star
3

htmlbars

A variant of Handlebars that emits DOM and allows you to write helpers that manipulate live DOM nodes
JavaScript
1,607
star
4

router.js

TypeScript
1,348
star
5

route-recognizer

A lightweight JavaScript library that matches paths against registered routes. It includes support for dynamic and star segments and nested handlers.
TypeScript
461
star
6

bloggr-client

The Source for the Ember Get Excited Video
JavaScript
310
star
7

conductor.js

JavaScript
150
star
8

simple-html-tokenizer

A lightweight JavaScript library for tokenizing non-`<script>` HTML expected to be found in the `<body>` of a document
TypeScript
85
star
9

learning-rust

Rust
67
star
10

kamino.js

Kamino.js is a library for passing data structures between sandboxed environments in the browser via `postMessage`.
JavaScript
48
star
11

ember-element-helper

Dynamic element helper for Glimmer templates.
JavaScript
42
star
12

ember-async-await-helper

Awaits a promise, then yields its result to a block. 👌
JavaScript
40
star
13

slackathon

A simple way to build slack interations inside a Rails app.
Ruby
31
star
14

MessageChannel.js

JavaScript
27
star
15

bound-templates.js

Bound Templates provides an interface similar to Polymer's MDV less invasively on more browsers (by using wrapper objects and an HTML parser written in JavaScript)
JavaScript
25
star
16

broccoli-typescript-compiler

TypeScript
24
star
17

json-normalizer

A small, lightweight library for normalizing JSON properties and values.
JavaScript
24
star
18

helix-rails

Helix for Rails
Ruby
22
star
19

libkit

JavaScript
17
star
20

ts-std

A standard library for TypeScript, extracted from Glimmer and other Tilde projects
TypeScript
10
star
21

buffoon

A protobuf library for Rust
Rust
10
star
22

selector-generator

A lightweight JavaScript library that generates CSS selectors from DOM nodes.
JavaScript
10
star
23

canal.js

Canal.js is a lightweight JavaScript library for creating channels, which are objects that represent a stream of events.
JavaScript
10
star
24

ember-control-flow-component

The superclass for all your "control-flow" component needs
9
star
25

ember-women

The Ember Women Helping Women Program
CSS
8
star
26

libcruby-sys

Rust
8
star
27

helix-website

Website for the Helix project
Ruby
8
star
28

kafka-librato-reporter

Report Kafka metrics to Librato
Java
8
star
29

sync.js

JavaScript
6
star
30

range-serializer

A lightweight JavaScript library that serializes/deserializes DOM range objects.
JavaScript
6
star
31

helix-flipper

A simple Rails app to get you started with Helix
Ruby
5
star
32

cfp-app

Rails app for managing a conference CFP
Ruby
5
star
33

jsframe.js

Convert JavaScript to (near) JavaScript/HTML polyglot.
JavaScript
4
star
34

oasis-website

Oasis.js helps you structure communication between multiple untrusted sandboxes
CSS
3
star
35

ember-template-string-interpolation

JavaScript
3
star
36

rustconf

Annual Rust Conference
HTML
3
star
37

ember-training-june-day2

JavaScript
3
star
38

discourse-refcount-resource-spike

JavaScript
2
star
39

skiplist.js

JavaScript
2
star
40

ember-training-nyc-june

Material for the Tilde training course in NYC
JavaScript
2
star
41

emberconf-meetups

A list of unofficial EmberConf meetups
2
star
42

ember-swappable-service

JavaScript
2
star
43

ember-better-attributes

An Ember plugin that provides a better attribute syntax, building on simple-html-tokenizer. It is expected to be merged into Ember in the future.
JavaScript
2
star
44

fastboot-test

JavaScript
1
star
45

rails_todo_app

Ruby
1
star
46

ruby-rust-musl

Test of building musl Rust extensions for Ruby
Ruby
1
star