Skip to content

Commit

Permalink
[Fix] Prevent merging __proto__ property
Browse files Browse the repository at this point in the history
  • Loading branch information
ljharb committed Oct 30, 2018
1 parent c8194ae commit d63002d
Showing 4 changed files with 52 additions and 5 deletions.
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
"eqeqeq": [2, "allow-null"],
"indent": [2, 2],
"max-depth": [2, 5],
"max-params": [2, 3],
"max-statements": [2, 29],
"multiline-comment-style": 0,
"no-continue": [1],
41 changes: 37 additions & 4 deletions lib/extend.js
Original file line number Diff line number Diff line change
@@ -10,6 +10,39 @@
* Port of jQuery.extend that actually works on node.js
*/
var is = require('is');
var has = require('has');

var defineProperty = Object.defineProperty;
var gOPD = Object.getOwnPropertyDescriptor;

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

// Return undefined instead of __proto__ if '__proto__' is not an own property
var getProperty = function getP(obj, name) {
if (name === '__proto__') {
if (!has(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];
};

// eslint-disable-next-line func-style
function extend() {
@@ -41,8 +74,8 @@ function extend() {
}
// 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) {
@@ -59,11 +92,11 @@ function extend() {
}

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

// Don't bring in undefined values
} else if (typeof copy !== 'undefined') {
target[name] = copy;
setProperty(target, name, copy);
}
}
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -12,7 +12,8 @@
],
"author": "dreamerslab <ben@dreamerslab.com>",
"dependencies": {
"is": "^3.1.0"
"has": "^1.0.3",
"is": "^3.2.1"
},
"devDependencies": {
"@ljharb/eslint-config": "^13.0.0",
12 changes: 12 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -571,3 +571,15 @@ test('deep clone; arrays are merged', function (t) {
t.deepEqual(target, expectedTarget, 'arrays are merged');
t.end();
});

test('__proto__ is merged as an own property', { skip: !Object.defineProperty }, 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 d63002d

Please sign in to comment.