Adding declarative, fined-grained "repair for hardening" of intrinsics.
MODIFY
src/bundle/createSES.js
src/bundle/createSES.js
@@ -24,6 +24,7 @@ import getAllPrimordials from './getAllPrimordials';
import whitelist from './whitelist';
import makeConsole from './make-console';
import makeMakeRequire from './make-require';
+import dataPropertiesToRepair from './dataPropertiesToRepair';
import makeRepairDataProperties from './makeRepairDataProperties';
const FORWARDED_REALMS_OPTIONS = ['transforms'];
@@ -118,6 +119,7 @@ You probably want a Compartment instead, like:
const r = Realm.makeRootRealm({ ...realmsOptions, shims });
+ // Build repairDataProperties() to be called before harden.
const makeRepairDataPropertiesSrc = `(${makeRepairDataProperties})`;
const repairDataProperties = r.evaluate(makeRepairDataPropertiesSrc)();
@@ -143,7 +145,7 @@ You probably want a Compartment instead, like:
anonIntrinsics,
);
- repairDataProperties(allIntrinsics);
+ repairDataProperties(allIntrinsics, dataPropertiesToRepair);
harden(allIntrinsics);
// build the makeRequire helper, glue it to the new Realm

ADD
src/bundle/dataPropertiesToRepair.js
+/**
+ * @fileoverview Exports {@code ses.dataPropertiesToRepair}, a recursively
+ * defined JSON record enumerating the optimal set of prototype properties
+ * on primordials that need to be repaired before hardening.
+ *
+ * //provides ses.dataPropertiesToRepair
+ * @author JF Paradis
+ */
+
+/**
+ * <p>The optimal set of prototype properties that need to be repaired
+ * before hardening is applied on enviromments subject to the override
+ * mistake.
+ *
+ * <p>Because "repairing" replaces data properties with accessors, every
+ * time a repaired property is accessed, the associated getter is invoked,
+ * which degrades the runtime performance of all code executing in a
+ * the repaired enviromment, compared to the non-repaired case. In order
+ * to maintain performance, we only repair the properties of objects
+ * for which hardening causes a breakage of their intended usage. There
+ * are two cases:
+ * <ul>Overriding properties on objects typically used as maps,
+ * namely {@code "Object"} and {@code "Array"}. In the case of arrays,
+ * a given program might not be aware that non-numerical properties are
+ * stored on the undelying object instance, not on the array. When an
+ * object is typically used as a map, we repair all of its prototype
+ * properties.
+ * <ul>Overriding properties on objects that provide defaults on their
+ * prototype that programs typically override by assignment, such as
+ * {@code "Error.prototype.message"} and {@code "Function.prototype.name"}
+ * (both default to "").
+ *
+ * <p>Each JSON record enumerates the disposition of the properties on
+ * some corresponding primordial object, with the root record containing:
+ * <ul>
+ * <li>The record for the global object.
+ * <li>The record for the anonymous intrinsics.
+ * </ul>
+ *
+ * <p>For each such record, the values associated with its property
+ * names can be:
+ * <ul>
+ * <li>Another record, in which case this property is simply left
+ * unrepaired and that next record represents the disposition of
+ * the object which is its value. For example, {@code "Object"}
+ * leads to another record explaining what properties {@code
+ * "Object"} may have and how each such property, if present,
+ * and its value should be repaired.
+ * <li>true, in which case this property is simply repaired. The
+ * value associated with that property is not traversed. For
+ * example, {@code "Function.prototype.name"} leads to true,
+ * meaning that the {@code "name"} property of {@code
+ * "Function.prototype"} should be repaired. If the property is
+ * already an accessor property, it is not repaired (because
+ * accessors are not subject to the override mistake).
+ * <li>"*", all properties on this object are repaired.
+ * </ul>
+ *
+ * <p>We factor out {@code true} into the variable {@code t} just to
+ * get a bit better compression from simple minifiers.
+ */
+
+const t = true;
+
+export default {
+ global: {
+
+ Object: {
+ prototype: '*',
+ },
+
+ Array: {
+ prototype: '*',
+ },
+
+ Function: {
+ prototype: {
+ name: t,
+ toString: t,
+ }
+ },
+
+ Error: {
+ prototype: {
+ message: t,
+ },
+ },
+ },
+
+ anonIntrinsics: {
+
+ TypedArray: {
+ prototype: '*',
+ },
+
+ GeneratorFunction: {
+ prototype: {
+ name: t,
+ toString: t,
+ }
+ },
+
+ AsyncFunction: {
+ prototype: {
+ name: t,
+ toString: t,
+ }
+ },
+
+ AsyncGeneratorFunction: {
+ prototype: {
+ name: t,
+ toString: t,
+ }
+ },
+
+ IteratorPrototype: '*',
+
+ },
+};

MODIFY
src/bundle/makeRepairDataProperties.js
src/bundle/makeRepairDataProperties.js
// https://github.com/google/caja/blob/master/src/com/google/caja/ses/repairES5.js
export default function makeRepairDataProperties() {
+
+ // Object.defineProperty is allowed to fail silently,
+ // use Object.defineProperties instead.
+
const {
defineProperties,
+ getOwnPropertyDescriptor,
getOwnPropertyDescriptors,
hasOwnProperty,
} = Object;
const { ownKeys } = Reflect;
- // Object.defineProperty is allowed to fail silently,
- // wrap Object.defineProperties instead.
- function defineProperty(obj, prop, desc) {
- defineProperties(obj, { [prop]: desc });
- }
-
/**
* For a special set of properties (defined below), it ensures that the
* effect of freezing does not suppress the ability to override these
@@ -59,89 +58,71 @@ export default function makeRepairDataProperties() {
if (hasOwnProperty.call(this, prop)) {
this[prop] = newValue;
} else {
- defineProperty(this, prop, {
+ defineProperties(this, {[prop]: {
value: newValue,
writable: true,
enumerable: desc.enumerable,
configurable: desc.configurable,
- });
+ }});
}
}
- defineProperty(obj, prop, {
+ defineProperties(obj, {[prop]: {
get: getter,
set: setter,
enumerable: desc.enumerable,
configurable: desc.configurable,
- });
+ }});
}
}
- /**
- * These properties are subject to the override mistake
- * and must be converted before freezing.
- */
- function repairDataProperties(intrinsics) {
- const { global: g, anonIntrinsics: a } = intrinsics;
-
- const toBeRepaired = [
- g.Object.prototype,
- g.Array.prototype,
- // g.Boolean.prototype,
- // g.Date.prototype,
- // g.Number.prototype,
- // g.String.prototype,
- // g.RegExp.prototype,
-
- g.Function.prototype,
- a.GeneratorFunction.prototype,
- a.AsyncFunction.prototype,
- a.AsyncGeneratorFunction.prototype,
-
- a.IteratorPrototype,
- // a.ArrayIteratorPrototype,
-
- // g.DataView.prototype,
-
- a.TypedArray.prototype,
- // g.Int8Array.prototype,
- // g.Int16Array.prototype,
- // g.Int32Array.prototype,
- // g.Uint8Array.prototype,
- // g.Uint16Array.prototype,
- // g.Uint32Array.prototype,
-
- g.Error.prototype,
- // g.EvalError.prototype,
- // g.RangeError.prototype,
- // g.ReferenceError.prototype,
- // g.SyntaxError.prototype,
- // g.TypeError.prototype,
- // g.URIError.prototype,
- ];
-
- // Promise may be removed from the whitelist
- // TODO: the toBeRepaired list should be prepared
- // externally and provided to repairDataProperties
- const PromisePrototype = g.Promise && g.Promise.prototype;
- if (PromisePrototype) {
- toBeRepaired.push(PromisePrototype);
- }
-
- // repair each entry
- toBeRepaired.forEach(obj => {
- if (!obj) {
+ function repairOneProperty(obj, prop) {
+ const desc = getOwnPropertyDescriptor(obj, prop);
+ if (!desc) {
return;
}
+ enableDerivedOverride(obj, prop, desc);
+ }
+
+ function repairAllProperties(obj) {
const descs = getOwnPropertyDescriptors(obj);
if (!descs) {
return;
}
- ownKeys(obj).forEach(prop =>
+ ownKeys(descs).forEach(prop =>
enableDerivedOverride(obj, prop, descs[prop]),
);
+ }
+
+ function walkRepairPlan(obj, plan) {
+ if (!obj) {
+ return;
+ }
+ ownKeys(plan).forEach(prop => {
+ const subPlan = plan[prop];
+ const subObj = obj[prop];
+ switch(subPlan) {
+
+ case true:
+ repairOneProperty(obj, prop);
+ break;
+
+ case '*':
+ repairAllProperties(subObj);
+ break;
+
+ default:
+ walkRepairPlan(subObj, subPlan);
+ }
});
}
+ function repairDataProperties(intrinsics, repairPlan) {
+ if (!repairPlan) {
+ return;
+ }
+ walkRepairPlan(intrinsics, repairPlan);
+ }
+
return repairDataProperties;
}

Adding declarative, fined-grained "repair for hardening" of intrinsics.