view tcb.js @ 31:d5087fb2367d

Added more to SafeWrapper.
author Atul Varma <varmaa@toolness.com>
date Mon, 22 Jun 2009 13:29:45 -0700
parents 82d800f2dcfc
children bda50240b295
line wrap: on
line source

// This script represents the Trusted Code Base (TCB) of the
// playground; it alone has access to all privileged functionality and
// can load SecurableModules as needed, exporting capabilities to them
// as necessary.

// This security function is called by the platform whenever a
// particular property needs to be accessed on a particular object.

function checkAccess(obj, id) {
  var frame = stack().caller;
  var isSuspicious = false;
  if (!(frame.filename == null ||
        frame.filename == "tcb.js")) {
    isSuspicious = true;
    print("access request from " + frame.filename + " on property '" + id +
          "' of " + obj);
  }
  if (id == 'caller') {
    if (frame.caller &&
        frame.caller.functionObject &&
        !(isSuspicious &&
          (functionInfo(frame.caller.functionObject).filename !=
           frame.filename))) {
      return frame.caller.functionObject;
    } else
      return null;
  }
  return lookupProperty(obj, id);
}

// This function is called by the platform whenever an uncaught exception
// occurs.

function handleError() {
  printTraceback(lastExceptionTraceback);
  print(lastException);
}

// This function uses the Python-inspired traceback functionality of the
// playground to print a stack trace that looks much like Python's.

function printTraceback(frame) {
  print("Traceback (most recent call last):");
  if (frame === undefined)
    frame = stack();
  var lines = [];
  while (frame) {
    var line = ('  File "' + frame.filename + '", line ' +
                frame.lineNo + ', in ' + frame.functionName);
    lines.splice(0, 0, line);
    frame = frame.caller;
  }
  print(lines.join('\n'));
}

// An example of some of the Python-inspired traceback functionality of
// the playground.

function throwError() {
  function innerThrowError() {
    var x = 1;
    throw new Error("hi");
  }
  innerThrowError();
}

try {
  throwError();
} catch (e) {
  print("caught an intentional error. local variables in scope chain: ");
  var scopeChain = lastExceptionTraceback.scopeChain;
  for (name in scopeChain)
    print("  " + name + ": " + scopeChain[name]);
}

// Load a sample SecurableModule and run some code in it.

function SafeWrapper(object) {
  var existingWrapper = getWrapper(object);
  if (existingWrapper)
    return existingWrapper;
  this._wrappee = object;
  return wrap(object, this);
}

SafeWrapper.prototype = {
  _maybeWrap: function(object) {
    switch (typeof(object)) {
    case "object":
    case "function":
      if (object == null)
        return null;
      return new SafeWrapper(object);
    case "string":
    case "boolean":
    case "undefined":
    case "number":
      return object;
    default:
      throw new Error("Unexpected type: " + typeof(object));
    }
  },

  convert: function(wrappee, wrapper, type) {
    switch (type) {
    case "function":
      if (typeof(wrappee) == "function")
        return wrapper;
      return undefined;
    case "undefined":
      // TODO: Malicious getter could destroy us here.
      return wrappee.toString();
    case "object":
      if (typeof(wrappee) == "object")
        return wrapper;
      return undefined;
    default:
      throw new Error("unexpected type: " + type);
    }
  },

  getProperty: function(wrappee, wrapper, id, defaultValue) {
    switch (id) {
    case "eval":
    case "prototype":
    case "caller":
    case "__proto__":
    case "__parent__":
      return null;
    default:
      // TODO: This will bypass any getters/setters.
      return this._maybeWrap(lookupProperty(wrappee, id));
    }
  },

  addProperty: function() {
    throw new Error("Can't add properties to this object.");
  },

  setProperty: function() {
    throw new Error("Can't set properties on this object.");
  },

  delProperty: function() {
    throw new Error("Can't delete properties on this object.");
  },

  equality: function(wrappee, wrapper, other) {
    return wrappee == other;
  },

  enumerate: function() {
    throw new Error("Enumeration is not implemented.");
  },

  iteratorObject: function(wrappee, wrapper, keysOnly) {
    if (keysOnly) {
      function keyIterator() {
        // TODO: Is this secure?
        for (name in wrappee)
          yield name;
      }
      return keyIterator();
    } else {
      function keyValueIterator() {
        // TODO: Is this secure?
        for (name in wrappee)
          yield [name, wrappee[name]];
      }
      return keyValueIterator();
    }
  },

  _callOrConstruct: function(wrapee, wrapper, thisObj, args) {
    if (typeof(this._wrappee) == "function") {
      var wrappedArgs = [];
      for (var i = 0; i < args.length; i++)
        wrappedArgs.push(this._maybeWrap(args[i]));
      var result;
      try {
        // TODO: What if the wrappee has a malicious getter for
        // apply()?
        result = this._wrappee.apply(this._maybeWrap(thisObj), wrappedArgs);
      } catch (e) {
        throw this._maybeWrap(e);
      }
      return this._maybeWrap(result);
    }
  },

  construct: function() {
    return this._callOrConstruct.apply(this, arguments);
  },

  call: function() {
    return this._callOrConstruct.apply(this, arguments);
  }
};

var module = require("sample-module.js", {blop: "hello",
                                          print: new SafeWrapper(print)});
module = new SafeWrapper(module);

(function() {
   print("module.foo() is " + module.foo());
   })();

// Some unit tests.

var wrapper = {};
var wrappee = {};
var wrapped = wrap(wrappee, wrapper);

if (unwrap({}) !== null)
  throw new Error("Unwrapping a non-wrapped object should return null!");

if (getWrapper({}) !== null)
  throw new Error("Getting the wrapper of a non-wrapped object should " +
                  "return null!");

if (unwrap(wrapped) !== wrappee ||
    unwrap(unwrap(wrapped)) !== null)
  throw new Error("Unwrapping doesn't work!");

if (getWrapper(wrapped) !== wrapper ||
    getWrapper(getWrapper(wrapped)) !== null)
  throw new Error("Getting the wrapper doesn't work!");