Skip to content

Commit

Permalink
Prevent mutation of Object.prototype and throw ObjectPrototypeMutatio…
Browse files Browse the repository at this point in the history
…nError

- Fix Hackerone issue  #788883
  • Loading branch information
podefr committed Sep 27, 2020
1 parent 816e430 commit 881cf37
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 3 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

### 3.0.0 - 27 SEP 2020

* Prevent mutation of Object.prototype to address a security issue. (Full report coming soon)

### 2.0.2 - 22 AUG 2020

* Upgrade dependencies to fix vulnerabilites reported by dependa bot
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,14 @@ nestedProperty.isIn(obj, "nested.0.property", obj.nested[0].property); // true
nestedProperty.isIn(obj, "nested.0.property", true); // true
```

### nestedProperty.ObjectPrototypeMutationError

__Note that it's not permitted to mutate Object.prototype:__

```
nestedProperty.set({}, '__protot__.a', 1); // throws nestedProperty.ObjectPrototypeMutationError
```

# LICENSE

MIT
23 changes: 21 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,24 @@
const ARRAY_WILDCARD = "+";
const PATH_DELIMITER = ".";

class ObjectPrototypeMutationError extends Error {
constructor(params) {
super(params);

this.name = "ObjectPrototypeMutationError";
}
}


module.exports = {
set: setNestedProperty,
get: getNestedProperty,
has: hasNestedProperty,
hasOwn: function (object, property, options) {
return this.has(object, property, options || { own: true });
},
isIn: isInNestedProperty
isIn: isInNestedProperty,
ObjectPrototypeMutationError
};

/**
Expand Down Expand Up @@ -120,6 +130,10 @@ function setNestedProperty(object, property, value) {

try {
return traverse(object, property, function _setNestedProperty(currentObject, currentProperty, segments, index) {
if (currentObject === Reflect.getPrototypeOf({})) {
throw new ObjectPrototypeMutationError("Attempting to mutate Object.prototype");
}

if (!currentObject[currentProperty]) {
const nextPropIsNumber = Number.isInteger(Number(segments[index + 1]));
const nextPropIsArrayWildcard = segments[index + 1] === ARRAY_WILDCARD;
Expand All @@ -138,7 +152,12 @@ function setNestedProperty(object, property, value) {
return currentObject[currentProperty];
});
} catch (err) {
return object;
if (err instanceof ObjectPrototypeMutationError) {
// rethrow
throw err;
} else {
return object;
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "nested-property",
"description": "Read, write or test a data structure's nested property via a string like 'my.nested.property'. It works through arrays and objects.'",
"version": "2.0.2",
"version": "3.0.0",
"homepage": "https://github.com/cosmosio/nested-property",
"license": "MIT",
"files": [
Expand Down
29 changes: 29 additions & 0 deletions test/set.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -369,4 +369,33 @@ describe("nested-property.set()", function () {
});
});
});

describe("When setting a property on an object's prototype", () => {
let newObj, protoObj;

beforeEach(function () {
protoObj = {};
newObj = Object.create(protoObj);

sut.set(newObj, "__proto__.a", 1);
});

it("Then mutates the prototype", function () {
expect(protoObj.hasOwnProperty("a")).to.be.true;
});
});

describe("When setting a property on Object.prototype", () => {
let newObj;

beforeEach(function () {
newObj = {};
});

it("Then throws an error", function () {
expect(function () {
sut.set(newObj, "__proto__.a", 1);
}).to.throw(sut.ObjectPrototypeMutationError);
});
});
});

0 comments on commit 881cf37

Please sign in to comment.