Skip to content

Commit

Permalink
[Fix] Prevent merging __proto__ property
Browse files Browse the repository at this point in the history
  • Loading branch information
mnespor authored and ljharb committed Apr 24, 2018
1 parent a689700 commit 0e68e71
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 4 deletions.
39 changes: 35 additions & 4 deletions index.js
Expand Up @@ -2,6 +2,8 @@

var hasOwn = Object.prototype.hasOwnProperty;
var toStr = Object.prototype.toString;
var defineProperty = Object.defineProperty;
var gOPD = Object.getOwnPropertyDescriptor;

var isArray = function isArray(arr) {
if (typeof Array.isArray === 'function') {
Expand Down Expand Up @@ -31,6 +33,35 @@ var isPlainObject = function isPlainObject(obj) {
return typeof key === 'undefined' || hasOwn.call(obj, key);
};

// If name is '__proto__', and Object.defineProperty is available, define __proto__ as an own property on target
var setProperty = function setProperty(target, options) {
if (defineProperty && options.name === '__proto__') {
defineProperty(target, options.name, {
enumerable: true,
configurable: true,
value: options.newValue,
writable: true
});
} else {
target[options.name] = options.newValue;
}
};

// Return undefined instead of __proto__ if '__proto__' is not an own property
var getProperty = function getProperty(obj, name) {
if (name === '__proto__') {
if (!hasOwn.call(obj, name)) {
return void 0;
} else if (gOPD) {
// In early versions of node, obj['__proto__'] is buggy when obj has
// __proto__ as an own property. Object.getOwnPropertyDescriptor() works.
return gOPD(obj, name).value;
}
}

return obj[name];
};

module.exports = function extend() {
var options, name, src, copy, copyIsArray, clone;
var target = arguments[0];
Expand All @@ -55,8 +86,8 @@ module.exports = function extend() {
if (options != null) {
// Extend the base object
for (name in options) {
src = target[name];
copy = options[name];
src = getProperty(target, name);
copy = getProperty(options, name);

// Prevent never-ending loop
if (target !== copy) {
Expand All @@ -70,11 +101,11 @@ module.exports = function extend() {
}

// Never move original objects, clone them
target[name] = extend(deep, clone, copy);
setProperty(target, { name: name, newValue: extend(deep, clone, copy) });

// Don't bring in undefined values
} else if (typeof copy !== 'undefined') {
target[name] = copy;
setProperty(target, { name: name, newValue: copy });
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions test/index.js
Expand Up @@ -626,3 +626,15 @@ test('non-object target', function (t) {

t.end();
});

test('__proto__ is merged as an own property', function (t) {
var malicious = { fred: 1 };
Object.defineProperty(malicious, '__proto__', { value: { george: 1 }, enumerable: true });
var target = {};
extend(true, target, malicious);
t.notOk(target.george);
t.ok(Object.prototype.hasOwnProperty.call(target, '__proto__'));
t.deepEqual(Object.getOwnPropertyDescriptor(target, '__proto__').value, { george: 1 });

t.end();
});

0 comments on commit 0e68e71

Please sign in to comment.