Skip to content
Permalink
Browse files Browse the repository at this point in the history
Fix issue presented in #14
  • Loading branch information
mrodrig committed Oct 6, 2020
1 parent 61b83a3 commit 3e2bb16
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 15 deletions.
3 changes: 0 additions & 3 deletions .travis.yml
Expand Up @@ -5,10 +5,7 @@ language: node_js
node_js:
- "14"
- "12"
- "11"
- "10"
- "9"
- "8"
sudo: false
before_script:
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -2,7 +2,7 @@
"author": "mrodrig",
"name": "doc-path",
"description": "A document path library for Node",
"version": "2.1.1",
"version": "2.1.2",
"repository": {
"type": "git",
"url": "http://github.com/mrodrig/doc-path.git"
Expand Down Expand Up @@ -37,7 +37,7 @@
"should": "13.2.3"
},
"engines": {
"node": ">=6.0"
"node": ">=10"
},
"license": "MIT"
}
27 changes: 19 additions & 8 deletions src/path.js
Expand Up @@ -30,29 +30,40 @@ function evaluatePath(document, keyPath) {
}

function setPath(document, keyPath, value) {
if (!document) {
throw new Error('No document was provided.');
} else if (!keyPath) {
throw new Error('No keyPath was provided.');
}

// If this is clearly a prototype pollution attempt, then refuse to modify the path
if (keyPath.startsWith('__proto__') || keyPath.startsWith('constructor')) {
return document;
}

return _setPath(document, keyPath, value);
}

function _setPath(document, keyPath, value) {
if (!document) {
throw new Error('No document was provided.');
}

let {indexOfDot, currentKey, remainingKeyPath} = computeStateInformation(keyPath);

// if (currentKey === '__proto__' || currentKey === 'prototype' && Object.prototype.hasOwnProperty.call(document, currentKey)) {
if (currentKey === '__proto__') {
// Refuse to modify anything on __proto__, return the document
return document;
} else if (indexOfDot >= 0) {
if (indexOfDot >= 0) {
// If there is a '.' in the keyPath, recur on the subdoc and ...
if (!document[currentKey] && Array.isArray(document)) {
// If this is an array and there are multiple levels of keys to iterate over, recur.
return document.forEach((doc) => setPath(doc, keyPath, value));
return document.forEach((doc) => _setPath(doc, keyPath, value));
} else if (!document[currentKey]) {
// If the currentKey doesn't exist yet, populate it
document[currentKey] = {};
}
setPath(document[currentKey], remainingKeyPath, value);
_setPath(document[currentKey], remainingKeyPath, value);
} else if (Array.isArray(document)) {
// If this "document" is actually an array, then we can loop over each of the values and set the path
return document.forEach((doc) => setPath(doc, remainingKeyPath, value));
return document.forEach((doc) => _setPath(doc, remainingKeyPath, value));
} else {
// Otherwise, we can set the path directly
document[keyPath] = value;
Expand Down
11 changes: 10 additions & 1 deletion test/tests.js
Expand Up @@ -218,11 +218,20 @@ describe('doc-path Module', function() {

it('should protect against prototype pollution via __proto__', (done) => {
doc = {};
path.setPath(doc, '__proto__.polluted', 'yes');
path.setPath(doc, '__proto__.polluted', 'prototype-polluted');
assert.equal(doc.__proto__.polluted, undefined);
assert.equal(doc.polluted, undefined);
assert.equal({}.polluted, undefined);
done();
});

it('should protect against prototype pollution via constructor', (done) => {
doc = {};
path.setPath(doc, 'constructor', 'prototype-polluted');
assert.equal(doc.constructor, Object);
path.setPath(doc, 'constructor.prototype.test', 'prototype-polluted');
assert.equal(doc.test, undefined);
done();
});
});
});

0 comments on commit 3e2bb16

Please sign in to comment.