Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: no more detached properties #473

Merged
merged 1 commit into from
Sep 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions packages/ses/src/enable-property-overrides.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -118,6 +135,4 @@ export default function enablePropertyOverrides(intrinsics) {

// Do the repair.
enableProperties('root', intrinsics, enablements);

return detachedProperties;
}
3 changes: 1 addition & 2 deletions packages/ses/src/lockdown-shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions packages/ses/test/frozen-primordials.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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();
});