fix: no more detached properties
MODIFY
packages/ses/src/enable-property-overrides.js
packages/ses/src/enable-property-overrides.js
@@ -18,6 +18,20 @@ function isObject(obj) {
return obj !== null && typeof obj === 'object';
}
+/**
+ * This symbol names a property that we're adding to getters that
+ * * identifies that the getter alleges that it was created to suppress the
+ * override mistake.
+ * * provides the original data property value as its value.
+ *
+ * This is intended for expert use and avoidance of accidental collision with
+ * normal code. But we are not trying to hide the symbol beyond that. Thus, we
+ * make it a registered symbol that can be reconstructed anywhere using the code
+ * `Symbol.for('originalValue')`. We no longer export it, so that's the most
+ * convenient way to obtain the symbol.
+ */
+const originalValueSymbol = Symbol.for('originalValue');
+
/**
* For a special set of properties (defined in the enablement plan), it ensures
* that the effect of freezing does not suppress the ability to override
@@ -35,17 +49,20 @@ function isObject(obj) {
// TODO exmplain parameters
export default function enablePropertyOverrides(intrinsics) {
- const detachedProperties = {};
-
function enable(path, obj, prop, desc) {
if ('value' in desc && desc.configurable) {
const { value } = desc;
- detachedProperties[path] = value;
-
function getter() {
return value;
}
+ // The presence of this symbol on an accessor's getter alleges that
+ // this accessor is a converted data property whose original value
+ // is the value of that property. By exposing it, the harden algorithm
+ // on encountering the getter will harden the getter function and also
+ // this value. Thereby preserving the same transitivity that harden would
+ // have had on the original data-property graph.
+ getter[originalValueSymbol] = value;
function setter(newValue) {
if (obj === this) {
@@ -118,6 +135,4 @@ export default function enablePropertyOverrides(intrinsics) {
// Do the repair.
enableProperties('root', intrinsics, enablements);
-
- return detachedProperties;
}

MODIFY
packages/ses/src/lockdown-shim.js
packages/ses/src/lockdown-shim.js
@@ -153,12 +153,11 @@ export function repairIntrinsics(
function hardenIntrinsics() {
// Circumvent the override mistake.
- const detachedProperties = enablePropertyOverrides(intrinsics);
+ enablePropertyOverrides(intrinsics);
// Finally register and optionally freeze all the intrinsics. This
// must be the operation that modifies the intrinsics.
lockdownHarden(intrinsics);
- lockdownHarden(detachedProperties);
// Having completed lockdown without failing, the user may now
// call `harden` and expect the object's transitively accessible properties

ADD
packages/ses/test/frozen-primordials.test.js
+import test from 'tape';
+import '../lockdown.js';
+import { getOwnPropertyDescriptor } from '../src/commons.js';
+
+const originalValueSymbol = Symbol.for('originalValue');
+
+test('check if override-protected primordials are frozen', t => {
+ lockdown();
+
+ // After removing the detachedProperties mechanism and before adding
+ // the originalValueSymbol mechanism, this test failed.
+ t.ok(Object.isFrozen(Object.prototype.toString));
+
+ // Just checking that reconstruction produces the *same* symbol
+ t.equals(originalValueSymbol, Symbol.for('originalValue'));
+
+ const desc = getOwnPropertyDescriptor(Object.prototype, 'toString');
+ t.equals(desc.get[originalValueSymbol], Object.prototype.toString);
+
+ t.end();
+});

fix: no more detached properties (#473)