shim repair functions
MODIFY
shim/src/accessors.js
shim/src/accessors.js
@@ -81,5 +81,3 @@ export function repairAccessors() {
makeLookupAccessor('__lookupGetter__', 'get');
makeLookupAccessor('__lookupSetter__', 'set');
}
-
-export const repairAccessorsShim = `(${repairAccessors})();`;

MODIFY
shim/src/functions.js
shim/src/functions.js
// https://github.com/google/caja/blob/master/src/com/google/caja/ses/startSES.js
// https://github.com/google/caja/blob/master/src/com/google/caja/ses/repairES5.js
-import { defineProperty, defineProperties, getPrototypeOf, setPrototypeOf } from './commons';
-
/**
- * The process to repair constructors:
- * 1. Obtain the prototype from an instance of the syntax
- * 2. Create a substitute noop constructor
- * 3. Replace its prototype property with the original prototype
- * 4. Replace its prototype property's constructor with itself
- * 5. Replace its [[Prototype]] slot with the noop constructor of Function
+ * This block replaces the original Function constructor, and the original
+ * %GeneratorFunction% %AsyncFunction% and %AsyncGeneratorFunction%, with
+ * safe replacements that throw if invoked.
+ *
+ * These are all reachable via syntax, so it isn't sufficient to just
+ * replace global properties with safe versions. Our main goal is to prevent
+ * access to the Function constructor through these starting points.
+
+ * After this block is done, the originals must no longer be reachable, unless
+ * a copy has been made, and funtions can only be created by syntax (using eval)
+ * or by invoking a previously saved reference to the originals.
*/
// todo: This function is stringified and evaluated outside of the primal
// realms and it currently can't contain code coverage metrics.
/* istanbul ignore file */
-function repairFunction(unsafeRec, functionName, functionDecl) {
- const { unsafeEval, unsafeFunction, unsafeGlobal } = unsafeRec;
+export function repairFunctions() {
+ const { defineProperty, getPrototypeOf, setPrototypeOf } = Object;
+ /**
+ * The process to repair constructors:
+ * 1. Create an instance of the function by evaluating syntax
+ * 2. Obtain the prototype from the instance
+ * 3. Create a substitute tamed constructor
+ * 4. Replace the original constructor with the tamed constructor
+ * 5. Replace tamed constructor prototype property with the original one
+ * 6. Replace its [[Prototype]] slot with the tamed constructor of Function
+ */
+ function repairFunction(name, declaration) {
let FunctionInstance;
try {
- // todo: pass the whole functionDecl in, rather than building a template
- // around it, make this look like createOptionalSyntax in intrinsics.js
- FunctionInstance = unsafeEval(functionDecl); // step 1
+ // Use Function() because eval() has issues with serializing functions under the esm module.
+ // TODO: investigate esm distortion of source code.
+ // eslint-disable-next-line no-new-func
+ FunctionInstance = Function(`return ${declaration}`)();
} catch (e) {
- if (e instanceof unsafeGlobal.SyntaxError) {
- // Prevent failure on platforms where generators are not supported.
+ if (e instanceof SyntaxError) {
+ // Prevent failure on platforms where async and/or generators are not supported.
return;
}
// Re-throw
@@ -35,51 +49,46 @@ function repairFunction(unsafeRec, functionName, functionDecl) {
}
const FunctionPrototype = getPrototypeOf(FunctionInstance);
- // Block evaluation of source when calling constructor on the prototype of functions.
- const TamedFunction = unsafeFunction('throw new Error("Not available");');
- // (new Error()).constructor does not inherit from Function, because Error
+ // Prevents the evaluation of source when calling constructor on the prototype of functions.
+ // eslint-disable-next-line no-new-func
+ const TamedFunction = Function('throw new Error("Not available");');
+ defineProperty(TamedFunction, 'name', { value: name });
+
+ // (new Error()).constructors does not inherit from Function, because Error
// was defined before ES6 classes. So we don't need to repair it too.
- // todo: what about (Error()).constructor ?
+
+ // (Error()).constructor inherit from Function, which gets a tamed constructor here.
// todo: in an ES6 class that does not inherit from anything, what does its
// constructor inherit from? We worry that it inherits from Function, in
// which case instances could give access to unsafeFunction. markm says
// we're fine: the constructor inherits from Object.prototype
- defineProperties(TamedFunction, {
- name: {
- value: functionName
- },
- prototype: {
- value: FunctionPrototype
- }
- });
+ // This line replaces the original constructor in the prototype chain
+ // with the tamed one. No copy of the original is peserved.
defineProperty(FunctionPrototype, 'constructor', { value: TamedFunction });
- if (TamedFunction !== unsafeFunction.prototype.constructor) {
+ // This line sets the tamed constructor's prototype data property to
+ // the original one.
+ defineProperty(TamedFunction, 'prototype', { value: FunctionPrototype });
+
+ if (TamedFunction !== Function.prototype.constructor) {
// Ensures that all functions meet "instanceof Function" in a realm.
- setPrototypeOf(TamedFunction, unsafeFunction.prototype.constructor);
+ setPrototypeOf(TamedFunction, Function.prototype.constructor);
}
}
-/**
- * This block replaces the original Function constructor, and the original
- * %GeneratorFunction% %AsyncFunction% and %AsyncGeneratorFunction%, with
- * safe replacements that preserve SES confinement. After this block is done,
- * the originals must no longer be reachable.
- */
-export function repairFunctions(unsafeRec) {
// Here, the order of operation is important: Function needs to be repaired
- // first since the other constructors need it. Note these are all reachable
- // via syntax, so it isn't sufficient to just replace global properties
- // with safe versions. Our main goal is to prevent access to the
- // unsafeFunction constructor through these starting points.
- repairFunction(unsafeRec, 'Function', '(function(){})');
- // "plain arrow functions" inherit from Function.prototype
- repairFunction(unsafeRec, 'GeneratorFunction', '(function*(){})');
- repairFunction(unsafeRec, 'AsyncFunction', '(async function(){})');
- repairFunction(unsafeRec, 'AsyncGeneratorFunction', '(async function*(){})');
-}
+ // first since the other repaired constructors need to inherit from the tamed
+ // Function function constructor.
+
// note: this really wants to be part of the standard, because new
// constructors may be added in the future, reachable from syntax, and this
-// list must be updated to match
+ // list must be updated to match.
+
+ repairFunction('Function', '(function(){})');
+ // "plain arrow functions" inherit from Function.prototype
+ repairFunction('GeneratorFunction', '(function*(){})');
+ repairFunction('AsyncFunction', '(async function(){})');
+ repairFunction('AsyncGeneratorFunction', '(async function*(){})');
+}

MODIFY
shim/src/unsafeRec.js
shim/src/unsafeRec.js
// this module must never be importable outside the Realm shim itself
import { getSharedGlobalDescs } from './stdlib';
-import { repairAccessors, repairAccessorsShim } from './accessors';
+import { repairAccessors } from './accessors';
import { repairFunctions } from './functions';
import { freeze } from './commons';
@@ -66,30 +66,23 @@ function createUnsafeRec(unsafeGlobal, allShims) {
});
}
-// todo: NEEDS COMMENT
-function sanitizeUnsafeRec(unsafeRec) {
- // Ensures that neither the legacy accessors nor the function constructors
- // can be used to escape the confinement of the evaluators to execute in the
- // context.
- repairFunctions(unsafeRec);
- // we repair the accessors by injecting a shim string, so it gets the right types
-}
+const repairAccessorsShim = `"use strict"; (${repairAccessors})();`;
+const repairFunctionsShim = `"use strict"; (${repairFunctions})();`;
// Create a new unsafeRec from a brand new context, with new intrinsics and a
// new global object
export function createNewUnsafeRec(allShims) {
const unsafeGlobal = getNewUnsafeGlobal();
- const unsafeRec = createUnsafeRec(unsafeGlobal, allShims);
- sanitizeUnsafeRec(unsafeRec);
- return unsafeRec;
+ unsafeGlobal.eval(repairAccessorsShim);
+ unsafeGlobal.eval(repairFunctionsShim);
+ return createUnsafeRec(unsafeGlobal, allShims);
}
// Create a new unsafeRec from the current context, where the Realm shim is
// being parsed and executed, aka the "Primal Realm"
export function createCurrentUnsafeRec() {
const unsafeGlobal = (0, eval)(unsafeGlobalSrc);
- const unsafeRec = createUnsafeRec(unsafeGlobal, [repairAccessorsShim]);
- // sanitizeUnsafeRec(unsafeRec); // todo: markm not sure we want to repair functions
repairAccessors();
- return unsafeRec;
+ repairFunctions();
+ return createUnsafeRec(unsafeGlobal, []);
}

shim repair functions