Loading ...
Sorry, an error occurred while loading the content.

225Re: [caplet] adsafe.js

Expand Messages
  • David-Sarah Hopwood
    Jun 6, 2008
    • 0 Attachment
      Adam Barth wrote:
      > On Tue, Jun 3, 2008 at 1:40 PM, Douglas Crockford <douglas@...> wrote:
      >> The first edition of adsafe.js is available at
      >> http://adsafe.org/adsafe.js. It still lacks dom wrappage and
      >> interwidget communication.
      >
      > Attached is a rough first draft of a safe DOM wrapper. The main idea
      > is that untrusted script views DOM nodes simply as integer handles.

      It would be easy to make the handles opaque:

      var nodes = [];

      function handleToNode(handle) {
      return handle.__node__;
      }

      function nodeToHandle(node) {
      if (!node) return null;

      // Check if we've seen this node before.
      var handle = node.__safe_dom_handle__;
      if (handleToNode(handle) === node)
      return handle;

      // Need to allocate a new handle and add it to the nodes array.
      nodes[nodes.length] = node;
      var handle = makeHandle(node);
      node.__safe_dom_handle__ = handle;
      return handle;
      }

      function makeHandle(node) {
      return {__node__: node};
      }

      function assertHandle(handle) {
      if (handle.__node__ !== void 0)
      throw INVALID_HANDLE_ERR;
      }

      [...]
      API.getElementById = function(id) {
      assertString(id);

      var n = nodes.length;
      for (var i = 0; i < n; ++i) {
      var node = nodes[i];
      if (node.getAttribute && node.getAttribute('id') === id)
      return node.__safe_dom_handle__;
      }
      return null;
      };

      This approach prevents a script from relying on the fact that you're
      implementing handles as integers. More importantly, though, it allows
      the handle objects to implement tamed DOM methods (using lexical
      encapsulation), allowing the API to look more like the original DOM:

      function makeHandle(node) {
      return {
      __node__: node,
      hasChildNodes: function () {
      return node.hasChildNodes();
      },
      getNodeType: function () {
      return node.nodeType;
      },
      getNodeName: function () {
      return node.nodeName;
      },
      getNodeValue: function () {
      return node.nodeValue;
      },
      getFirstChild: function () {
      return nodeToHandle(node.firstChild);
      },
      getLastChild: function () {
      return nodeToHandle(node.lastChild);
      },
      getNextSibling: function () {
      if (node === root_node) return null;
      return nodeToHandle(node.nextSibling);
      },
      getPreviousSibling: function () {
      if (node === root_node) return null;
      return nodeToHandle(node.parentNode);
      }
      };
      }

      (It's also possible to support properties like 'nodeValue' directly rather
      than via getter methods, but in that case mutable properties would have to
      be kept in sync whenever the underlying node changed -- unless you rely on
      Mozilla or ES3.1 property getters.)

      Note that, unlike in Caja or Jacaranda, these methods cannot refer to
      'this', because ADsafe does not prevent obtaining a property that holds
      a function and then calling it with 'this' bound to the global object.
      ADsafe also does not prevent setting arbitrary properties (unless they
      start with _ or are blacklisted), so there is no encapsulation between
      objects created by a given script, including handle objects. However,
      encapsulation is not strictly needed for ADsafe's threat model.
      Caja, Cajita and Jacaranda incur much greater implementation complexity
      (less so for Jacaranda, but still more than ADsafe) in order to support
      this encapsulation.

      --
      David-Sarah Hopwood
    • Show all 7 messages in this topic