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

Another subset worthing considering?

Expand Messages
  • Kris Zyp
    I have been thinking about capabilities-based security and ES subsets like ADsafe and Caja, and was thinking about another subset that is intriguing to me and
    Message 1 of 1 , Feb 27, 2008
    • 0 Attachment
      I have been thinking about capabilities-based security and ES subsets like ADsafe and Caja, and was thinking about another subset that is intriguing to me and I was wondering if you all think it might be worth exploring. Basically the idea is to take ADsafe and safely add the ability to use |this| for better OOP support. This can be done by having a root syntax of object literal and array literals (similar to JSON) and allowing functions to be defined as well as other JSON values. A runtime sanitizer would be invoked on the code immediately after evaluation that quickly walks the tree and wraps all functions to add runtime checks to ensure that the functions are always invoked with the correct |this| object: the parent object in JSON graph, or an object which has a prototype of the parent object in the JSON graph. If the incorrect scope is used, an error is thrown, and so |this| can be safely used in these functions, as they act like bound methods. Only functions that are values in the outer object literals can be assured this safety, inner functions would still need to be denied use of the |this|.
      This approach would be very similiar to ADsafe. Static analysis is used to reject or accept valid scripts/objects. However, a runtime processing is still needed, but it is a very small, fast check. It only requires walking the declared object graph. There is no need to do parsing/AST evaluation. In fact, the code of runtime checker is included below.
      For this remaining explanation of this subset, I am calling this subset "Capsol" (CAPability-based Secure Object Literals):
      Advantages:
      • A Capsol is an expression at the root of AST, rather than a script/set of statement, the result of a capsol is a value.
        • They can be used in a JSON expression as a value (in lieu of string, number, object, etc.)
        • I believe this provides a more appealing form of initial introduction. I believe in Caja and ADsafe, the primary object introductions are made by creating a safe scope object, and implicity introducing this object to the script and allowing it to work with this object (utilize references in that safe scope). In a Capsol, the initial object introduction is done by passing the evaluating Capsol script to the container script. The container script that gets to play the initiator role with the limited capability object, deciding which properties to access or which methods to invoke. A safe scope can still be introduced to the Capsol, but it is not the primary initial introduction.
        • On initiation, the Capsol can not interact with environment at all (even a safe scope) and no turing complete execution paths can be made at evaluation time by the Capsol. The turing complete JavaScript execution and environmental interactions within Capsols can only be executed when the container script actually explicity invokes a method on the Capsol activated object.
      • Capsol strictly supersets JSON (arguably neither ADsafe, Caja, or even JavaScript do this, since an object literal by itself is not a valid script). On a less strict level it subsets Caja and JavaScript. This means a Capsol loader can also load JSON without any extra effort, since everything is loaded as an expression/value.
        This could be very valuable for future efforts to embed secure ecmascript in ES4 (or other versions), because JSON parsing and secure ecmascript evaluation could be a single mechanism. Since JSON is a simple form (subset) of Capsols, a evaluator that can handle Capsol would also be able to handle JSON, and still preserve the important properties of a JSON parser (no arbitrary code is executed on evaluation, an object/array structure is returned). Rather than having JSON.parse() and secureEval, there could be a single entry point (it may still be desirable to have an optional argument to limit to JSON).
      • Provides more flexible, JavaScript-esque object oriented programming capabilities because |this| is accessible. The capability still exists for normal JavaScript emulation of private variables, and abilty to call other public methods.
      • The declarative nature of Capsols provides a structure which can much more easily be statically analyzed.
      • Persistence/serialization can be easily reasoned about. A Capsol can be serialized (without loss of information, except mutations in the explicity defined transient variables) and can maintain this property by following some simple rules.
      There are several entities that compose the grammar in the Capsol subset:
       
      Capsol - is a Public Capsol or Semi-Public Capsol
       
      Public Capsol - is any valid JavaScript/JSON primitive (a string, number, boolean, or null) or a Capsol Object or a Capsol Array.
       
      Capsol Object - is an object literal (JSON Object) where the values are Capsol Values.
       
      Capsol Array - is an array literal (JSON Array ) where the values are Capsol Values.
       
      Capsol Value - a Public Capsol (which can be a primitive), a JavaScript function, or a Capsol Activation. If it is a function, the body of the function is a Capsol Method Block.
       
      Capsol Method Block - A Capsol method block generally follows the rules of ADsafe with the notable exception that |this| is allowed in the function as long as it is not enclosed by an inner function and Capsol Activations are allowed. Any function defined inside a Capsol Method Block must have a body that conforms to a Capsol Function Block.
       
      Capsol Function Block - This follows the rules of ADsafe except that Capsol Activations are also allowed.
       
      Semi-Public Capsol (this is somewhat of an experimental idea in providing a privatization mechanism, not sure if it is the right way) - This has the following syntax:
      function() {
      var <variable name> = <JSON value or a function with Capsol Function Block body, or a Capsol Activation >, ...;
      return <Public Capsol>; }
       
      The var section should follow standard JavaScript var statement syntax except that only JSON values or functions with Capsol Function Blocks bodies or Capsol Activations are allowed as values. The variables declared in this section should be allowed to be accessed and modified by the function blocks within this Capsol. The variables declared here provide a means for private (inaccessible except to functions within the Capsol) static transient variables for the Capsol.
       
      Capsol Activation - A Capsol Activation must follow this syntax:
      Capsol(<Capsol>)
       
       
      The Capsol function that is called when a Capsol Activation is invoked to provide the proper runtime checks:
      function Capsol(it) {
      // sanitize the Capsol object graph by ensuring that all methods
      // that can have |this| in them, may only be invoked with the appropriate this
          if (it && !it.__safe__) { // check to see if it has already been sanitized
              if (typeof it == 'function') { // A root function is not allowed in Capsol, so if
                  // it is a root function, that means it is a Semi-Public Capsol and we need
                  // to execute it to get the object structure. It is important to note
                  // that we could save a "toString" of the function in order to preserve
                  // the closure/private variables declaration for later reserialization
                  it = it();
              }
              it.__safe__ = true;
              for (var i in it) {
                  var value = it[i];
                  if (typeof value == 'function') {
                      it[i] = function() { // execute the function as long as it is the original |this| or an instance of |this|
                          if (this === it || it.isPrototypeOf(this)) {
                              return arguments.callee.__rawMethod__.apply(this,arguments);
                          }
                          throw Error("Method called on wrong object");
                      }
                      it[i].__rawMethod__ = value;
                  }
                  else {
                      Capsol(value); //recursively walk the tree
                  }
              }
          }
          return it;
      }
       
      The evalCapsol function could look like:
      function evalCapsol(capsolString) {
          // we are assuming it has been syntactically verified by jslint-ish mechanism already
          return Capsol(eval("(" + capsolString + ")"));
      }
       
       
      An example Capsol:
      {// some math stuff
          name: "Math stuff",
          add : function(a,b) {
              return a + b;
          }
      }
       
      Or a more advanced Capsol (Semi-Public Capsol because of the var declaration):
      function() {
        var Pi = 3.14159,
          topArea = function(self) { // calculate the area of the top (or bottom) of the cylinder 
              return self.radius * self.radius * Pi;
          },
          sideArea = function(self) { // calculate the area of the side of the cylinder 
              return 2 * self.radius * PI * self.height;
          };
        return {// A Safe Cylinder Capsol Object
          volume : function() { // calculate the volume of a the cylinder
              return topArea(this) * this.height;
          },
          totalArea : function() {// calculate the total area of the cylinder 
              return topArea(this) + sideArea(this);
          },
          isBiggerThan : function(otherCylinder) {
              return this.volume() > otherCylinder.volume();
          },
          height : 1, // set some defaults
          radius : 1
        }
      }
       
      An example usage of this Capsol:
       
      var cylinder = evalCapsol(safeCylinderString); // the text above
      cylinder.radius = 2.5;
      cylinder.volume() -> returns 19.63...
       
      They could also be combined and provide a constructor for the cylinder in the "math stuff" Capsol:
       
      {// some math stuff
          name: "Math stuff",
          add : function(a,b) {
              return a + b;
          },
          Cylinder : function() {
            return Capsol(function() {
                var Pi = 3.14159,
                  topArea = function(self) { // calculate the area of the top (or bottom) of the cylinder 
                      return self.radius * self.radius * Pi;
                  },
                  sideArea = function(self) { // calculate the area of the side of the cylinder 
                      return 2 * self.radius * PI * self.height;
                  };
                return {// A Safe Cylinder Capsol Object
                  volume : function() { // calculate the volume of a the cylinder
                      return topArea(this) * this.height;
                  },
                  totalArea : function() {// calculate the total area of the cylinder 
                      return topArea(this) + sideArea(this);
                  },
                  isBiggerThan : function(otherCylinder) {
                      return this.volume() > otherCylinder.volume();
                  },
                  height : 1, // set some defaults
                  radius : 1
                }
              });
          }
      }
       
      An example usage of this Capsol:
       
      var mathStuff = evalCapsol(safeCylinderString); // the text above
      // we can now use cylinder object and it's methods directly... or we can beget some instances:
      var cylinderA = mathStuff.Cylinder();
      var cylinderB = mathStuff.Cylinder();
      cylinderA.height = 5;
      cylinderB.radius = 2.5;
      cylinderA.isBiggerThan(cylinderB) -> returns false
       
      Thoughts? Worth exploring more?
      Thanks,
      Kris
       
    Your message has been successfully submitted and would be delivered to recipients shortly.