Non greedy in 95 bytes
(function(O,P){P in O||(O[P]=function(o,p){o.__proto__=p;return o})})(Object,'setPrototypeOf');
Or via self reassignment in 91 bytes
(function(O,P){O[P]=O[P]||function(o,p){o.__proto__=p;return o}})(Object,"setPrototypeOf");
Or direct call, 65 bytes, for you closure!
var p=Object.setPrototypeOf||function(o,p){o.__proto__=p;return o};
I am bored by explaing with words, it's time to show some example.
All of this is because developers are both lazy and stubborns and prefer a dirty and risky way instead of embrace and propose the usage of an explicit Object.setPrototypeOf(obj, source):obj
which is truly easy to polyfill:
var proto = Object.setPrototypeOf || function (o, p) {
// for jusrassic browsers still adopting this mess
o.__proto__ = p;
return o;
};
// instead of
{__proto__: proto}
proto({}, proto);
// even smaller
Not only performance is preserved, above example is conveniente against obj[key] = value
too because it's an explicit operation with an explicit developer intent.
It is not possible that by accident or by malicious code your objects could suddenly change inherithance.
How? Here different examples
The very first example is about setting a generic object property.
var o = {
setValue: function (key, value) {
this[key] = value;
}
};
// third parts, user input
// whatever external or internal
// non sanitized source
var genericKey = '__proto__';
// from now on, every object inheriting o
// and the `o` object included are affected
o.setValue(genericKey, null);
// bye bye o and all instances
alert(o instanceof Object); // false
Bear in mind, o
could be any Constructor.prototype object.
Most emitters are based into direct property set into objects rather than empty dictionaries whcih means these are all unsafe.
node.js as example uses an object as this._events
and the same is on the web.
Specially on latter one, a malicious code could change that prototype injecting as proottype a Proxy
or an object fulls of setters and getters and intercept basically all events and data fired through the library in a completely transparent way.
To protect your code against this kind of attack use this._events = Object.create(null);
instead so there's no way any third parts or malicious code could hook into your events. This is also better if you want to use any sort of name for your events, included those present in the Object.protoype
if needed.
Otherwise you can check that every time your library adopted by third parts does not set a key string which content is __proto__
Using _.extend(a, b)
with this library might result in different behavior.
Mobile and desktop Chrome browsers will be safe with next example while all new desktop Browsers following ES6 will break the source obj
object.
var dict = Object.create(null),
obj = {generic: 'object'};
// a wild key with a wild special property
var key = '__proto__';
// malicious code or any sor tof code
// that uses a dictionary the way it's meant to be used
dict[key] = Array.prototype;
// now a call to underscore.js
_.extend(obj, dict);
// the object is not the object we know anymore
alert(obj instanceof Array); // true
Bear in mind __proto__
is just a key as any other key and dictionaries are not aware of any special key indeed.
However, the bad part is that any object could become a Dictionary following the very first example.
The problem in latter example is that method won't be consitent but the library goal is to make these kind of operations consistents and as safe as possible cross platform.
The reason developers used __proto__
is because it was not enumerable but this was reflected into Object.create(null)
too while now, accordingly with specs, dictionaries will have that as enumerable property (which is correct and expected behavior)
Remember ES5 introduced "use strict";
directive to avoid developers leaking by accident global variables because of undefined or null this
context while calling a generic function? The issue here is exactly the same, the possibility, by accident, to change an entire application behavior (security) and the fact your library in 3rd parts hands is not secure anymore (included exposed APIs)
Again use Object.setPrototypeOf()
is not slower, is not problematic in term of size, is just a hint for TC39 to see developers don't want such magic property in the middle.
This property is much worst than any non enumerable bug in old IEs ... you don't want this mess in the specs, do you?
Thanks for your listening and understanding.
Well, my battle is all over but will end up hopefully here 'cause I don't know anymore how to explain this issue for both security and consistency with ECMAScript.
- JS proto Shenanigans
- 5 Reasons You Should Avoid proto
- Yet Another Reason To Drop proto
- Investigate potential
__proto__
issues
Happy easter