ses
Version:
Hardened JavaScript for Fearless Cooperation
243 lines (217 loc) • 8.75 kB
JavaScript
import { toStringTagSymbol, iteratorSymbol } from './commons.js';
/**
* @module Exports {@code enablements}, a recursively defined
* JSON record defining the optimum set of intrinsics properties
* that need to be "repaired" before hardening is applied on
* enviromments subject to the override mistake.
*
* @author JF Paradis
* @author Mark S. Miller
*/
/**
* <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 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 normal intended usage.
*
* There are three unwanted cases:
* <ul>
* <li>Overriding properties on objects typically used as records,
* namely {@code "Object"} and {@code "Array"}. In the case of arrays,
* the situation is unintentional, a given program might not be aware
* that non-numerical properties are stored on the underlying object
* instance, not on the array. When an object is typically used as a
* map, we repair all of its prototype properties.
* <li>Overriding properties on objects that provide defaults on their
* prototype and that programs typically set using an assignment, such as
* {@code "Error.prototype.message"} and {@code "Function.prototype.name"}
* (both default to "").
* <li>Setting-up a prototype chain, where a constructor is set to extend
* another one. This is typically set by assignment, for example
* {@code "Child.prototype.constructor = Child"}, instead of invoking
* Object.defineProperty();
*
* <p>Each JSON record enumerates the disposition of the properties on
* some corresponding intrinsic object.
*
* <p>For each such record, the values associated with its property
* names can be:
* <ul>
* <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 (which is needed
* when inheriting from @code{Function} and setting the subclass's
* {@code "prototype.name"} property). If the property is
* already an accessor property, it is not repaired (because
* accessors are not subject to the override mistake).
* <li>"*", in which case this property is not repaired but the
* value associated with that property are traversed and repaired.
* <li>Another record, in which case this property is not repaired
* and that next record represents the disposition of the object
* which is its value. For example,{@code "FunctionPrototype"}
* leads to another record explaining which properties {@code
* Function.prototype} need to be repaired.
*/
/**
* Minimal enablements when all the code is modern and known not to
* step into the override mistake, except for the following pervasive
* cases.
*/
export const minEnablements = {
'%ObjectPrototype%': {
toString: true,
},
'%FunctionPrototype%': {
toString: true, // set by "rollup"
},
'%ErrorPrototype%': {
name: true, // set by "precond", "ava", "node-fetch"
},
'%IteratorPrototype%': {
toString: true,
// https://github.com/tc39/proposal-iterator-helpers
constructor: true,
// https://github.com/tc39/proposal-iterator-helpers
[toStringTagSymbol]: true,
},
};
/**
* Moderate enablements are usually good enough for legacy compat.
*/
export const moderateEnablements = {
'%ObjectPrototype%': {
toString: true,
valueOf: true,
},
'%ArrayPrototype%': {
toString: true,
push: true, // set by "Google Analytics"
concat: true, // set by mobx generated code (old TS compiler?)
[iteratorSymbol]: true, // set by mobx generated code (old TS compiler?)
},
// Function.prototype has no 'prototype' property to enable.
// Function instances have their own 'name' and 'length' properties
// which are configurable and non-writable. Thus, they are already
// non-assignable anyway.
'%FunctionPrototype%': {
constructor: true, // set by "regenerator-runtime"
bind: true, // set by "underscore", "express"
toString: true, // set by "rollup"
},
'%ErrorPrototype%': {
constructor: true, // set by "fast-json-patch", "node-fetch"
message: true,
name: true, // set by "precond", "ava", "node-fetch", "node 14"
toString: true, // set by "bluebird"
},
'%TypeErrorPrototype%': {
constructor: true, // set by "readable-stream"
message: true, // set by "tape"
name: true, // set by "readable-stream", "node 14"
},
'%SyntaxErrorPrototype%': {
message: true, // to match TypeErrorPrototype.message
name: true, // set by "node 14"
},
'%RangeErrorPrototype%': {
message: true, // to match TypeErrorPrototype.message
name: true, // set by "node 14"
},
'%URIErrorPrototype%': {
message: true, // to match TypeErrorPrototype.message
name: true, // set by "node 14"
},
'%EvalErrorPrototype%': {
message: true, // to match TypeErrorPrototype.message
name: true, // set by "node 14"
},
'%ReferenceErrorPrototype%': {
message: true, // to match TypeErrorPrototype.message
name: true, // set by "node 14"
},
// https://github.com/endojs/endo/issues/550
'%AggregateErrorPrototype%': {
message: true, // to match TypeErrorPrototype.message
name: true, // set by "node 14"?
},
'%PromisePrototype%': {
constructor: true, // set by "core-js"
},
'%TypedArrayPrototype%': '*', // set by https://github.com/feross/buffer
'%Generator%': {
constructor: true,
name: true,
toString: true,
},
'%IteratorPrototype%': {
toString: true,
// https://github.com/tc39/proposal-iterator-helpers
constructor: true,
// https://github.com/tc39/proposal-iterator-helpers
[toStringTagSymbol]: true,
},
};
/**
* The 'severe' enablement are needed because of issues tracked at
* https://github.com/endojs/endo/issues/576
*
* They are like the `moderate` enablements except for the entries below.
*/
export const severeEnablements = {
...moderateEnablements,
/**
* Rollup (as used at least by vega) and webpack
* (as used at least by regenerator) both turn exports into assignments
* to a big `exports` object that inherits directly from
* `Object.prototype`. Some of the exported names we've seen include
* `hasOwnProperty`, `constructor`, and `toString`. But the strategy used
* by rollup and webpack potentionally turns any exported name
* into an assignment rejected by the override mistake. That's why
* the `severe` enablements takes the extreme step of enabling
* everything on `Object.prototype`.
*
* In addition, code doing inheritance manually will often override
* the `constructor` property on the new prototype by assignment. We've
* seen this several times.
*
* The cost of enabling all these is that they create a miserable debugging
* experience specifically on Node.
* https://github.com/Agoric/agoric-sdk/issues/2324
* explains how it confused the Node console.
*
* (TODO Reexamine the vscode situation. I think it may have improved
* since the following paragraph was written.)
*
* The vscode debugger's object inspector shows the own data properties of
* an object, which is typically what you want, but also shows both getter
* and setter for every accessor property whether inherited or own.
* With the `'*'` setting here, all the properties inherited from
* `Object.prototype` are accessors, creating an unusable display as seen
* at As explained at
* https://github.com/endojs/endo/blob/master/packages/ses/docs/lockdown.md#overridetaming-options
* Open the triangles at the bottom of that section.
*/
'%ObjectPrototype%': '*',
/**
* The widely used Buffer defined at https://github.com/feross/buffer
* on initialization, manually creates the equivalent of a subclass of
* `TypedArray`, which it then initializes by assignment. These assignments
* include enough of the `TypeArray` methods that here, the `severe`
* enablements just enable them all.
*/
'%TypedArrayPrototype%': '*',
/**
* Needed to work with Immer before https://github.com/immerjs/immer/pull/914
* is accepted.
*/
'%MapPrototype%': '*',
/**
* Needed to work with Immer before https://github.com/immerjs/immer/pull/914
* is accepted.
*/
'%SetPrototype%': '*',
};