Skip to content

Instantly share code, notes, and snippets.

@Jamesernator
Last active June 16, 2022 06:28
Show Gist options
  • Save Jamesernator/dc9831f9c13d17ac9a75d1fe25a2e015 to your computer and use it in GitHub Desktop.
Save Jamesernator/dc9831f9c13d17ac9a75d1fe25a2e015 to your computer and use it in GitHub Desktop.
Harden

Harden - Fixing the override mistake

The override mistake

The override mistake is a problem introduced with ES5's ability to mark properties as non-writable. When a property on the prototype of an object is marked non-writable, it makes the property unaddable on the object.

A notable example of this is if we freeze (which marks all properties as non-writable) on Object.prototype.

Object.freeze(Object.prototype);

When we do this, it becomes impossible to set toString() on any objects that don't already have a .toString().

e.g. the following fails:

"use strict";
Object.freeze(Object.prototype);
const newObject = {};
newObject.toString = () => "Some string"; // TypeError

This is of particular concern to the SES proposal, which needs to freeze all builtins in order to guarantee security properties.

Solution: Overridable writable properties

In this proposal we propose a new descriptor member called overridable, this member may only be present when writable is set to false.

When overridable is present, non-writable properties on the prototype will be ignored when attempting to set them.

For example the following will print 10:

"use strict";
const proto = {};
Object.defineProperty(proto, 'example', { value: 5, writable: false, overridable: true });
const object = { __proto__: proto };
object.example = 10;
console.log(object.example);

Object.harden

For SES, we need to be able to able to freeze entire objects, but without creating the problems of the override mistake. As such a new function will be provided called Object.harden, this method acts just like Object.freeze, except it sets overridable: true on all descriptors (that are configurable).

For example, the following will just work now:

"use strict";
Object.harden(Object.prototype);
const object = {};
object.toString = () => "Hello world!";
console.log(object.toString()); // Hello world!

QUESTION: Should this simply be an option to freeze rather than a new method?

Hazard: Existing code relying on descriptors

If we expose overridable: boolean as a new property on descriptors returned from Object.getOwnPropertyDescriptor(s), will existing code break?

If so, should we add a new method to get overridable to Object (e.g. Object.getIsOverridable(object, name)), or should we expose overridable only when it is set to true.

@Jamesernator
Copy link
Author

I should point out one reason something like [[OveridableProperties]] is needed on the prototype rather than just unconditionally returning true in cases where there's an unwritable property on the prototype is that this changes the behaviour of existing code (and that exact solution has been previously proposed and rejected).

Rather by having Object.harden or whatever as an opt-in, existing code will be unchanged, but new code can still freely opt-in to overridable properties.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment