Mercurial > spidermonkey-playground
view tcb.js @ 34:afca92a2e72e
enumeration is now supported on wrappers via JSNewEnumerateOp.
author | Atul Varma <varmaa@toolness.com> |
---|---|
date | Mon, 22 Jun 2009 15:33:18 -0700 |
parents | bda50240b295 |
children | cfdbaadf21b8 |
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: Is this secure? return this._maybeWrap(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(wrappee, wrapper) { // TODO: Is this secure? for (name in wrappee) yield name; }, 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()); })(); for (name in module) print(name); print("enumerate is " + enumerate(module)); // 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!");