Nuking misleading properties in `Object.getOwnPropertyDescriptor`
bruant.d at gmail.com
Wed Mar 13 08:59:33 PDT 2013
Le 13/03/2013 16:26, Nathan Wall a écrit :
> David Bruant wrote:
>> Tom Van Cutsem wrote:
>>> To my mind, the blame for the breakage lies with `Object.prototype`
>>> being mutated by the third-party script, not with property descriptors
>>> inheriting from Object.prototype. Thus, a fix for the breakage should
>>> address that directly, rather than tweaking the design of property
>>> descriptors, IMHO.
>> I agree.
>> would be to freeze all built-ins like SES  does. (In the future,
>> it could even make sense to add a CSP  directive for that)
>> If necessary, the application can first enhance the environment by
>> adding polyfills/libraries and such, but that's pretty much the only
>> thing that's acceptable to run before freezing everything.
> Hey David and Tom. This is good advice for application authors, but I don't work at the application level; I write libraries. I don't want to freeze everything because I want to leave the environment open to monkey-patching and shimming by other libraries and the application authors. So this isn't an option for me.
As a library author, in theory, you don't know in which environment your
code is going to be executed (that is maybe the environment has been
modified or not), so I think one assumption has to be made:
Either you assume the library runs in a non-changing environment
(whether the client has frozen itself or just decided not to change
anything) or you assume it is a battlefield where anything can happen
and try to capture a reference to all available built-ins at first load
as well as being defensive against potential changes to the environment
(like Object.prototype in your case).
>> "what if an attacker switches Array.prototype.push and Array.prototype.pop?"
> These are issues that are easy to address by using stored late-bound function references rather than methods and array-likes instead of true arrays.
> var push = Function.prototype.call.bind(Array.prototype.push),
> arrayLike = Object.create(null);
> arrayLike.length = 0;
> push(arrayLike, 'item-1');
> As long as the environment is correct when my script initializes, I get all methods I need to use stored inside my library's closure. Freezing isn't needed.
> It's also possible to write around the `defineProperty` problem by converting the descriptor into a prototype-less object. However, I actually encountered some performance problems with this. I was able to improve the performance by only dropping the prototype when necessary (as long as `get`, `set`, `value` or `writable` haven't been added to `Object.prototype`, it's not necessary). However, as a matter of principle, my argument is that `Object.getOwnPropertyDescriptor` should, at the bare minimum, return a descriptor that can be known to work in `Object.defineProperty`. If `Object.defineProperty` doesn't accept it, then you `getOwnPropertyDescriptor` didn't really give me a valid descriptor.
> I think that this behavior (1) limits the creativity of developers to define properties like `Object.prototype.get`
I don't think we should consider adding a Object.prototype.get property
as creativity. For instance, proxies have an optional "get" trap, so
things can become confusing quickly.
Other properties also have other meanings for other objects. For
instance JSON.parse creates objects with Object.prototype as
[[Prototype]], so custom Object.prototype properties may be confusing in
"myData.someProp" cases. It's possible, but annoying to prefix every
[[Get]] with hasOwnProperty checks.
If someone tries to polyfill Array.prototype.contains, they may test
'contains' in Array.prototype
If Object.prototype.contains is defined, this can mistakenly return
Writing defensive code is a serious relearning of how to write for the
More information about the es-discuss