Skip to content

Commit 3e2bb16

Browse files
committed
Fix issue presented in #14
1 parent 61b83a3 commit 3e2bb16

File tree

5 files changed

+32
-15
lines changed

5 files changed

+32
-15
lines changed

Diff for: .travis.yml

-3
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ language: node_js
55
node_js:
66
- "14"
77
- "12"
8-
- "11"
98
- "10"
10-
- "9"
11-
- "8"
129
sudo: false
1310
before_script:
1411
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter

Diff for: package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"author": "mrodrig",
33
"name": "doc-path",
44
"description": "A document path library for Node",
5-
"version": "2.1.1",
5+
"version": "2.1.2",
66
"repository": {
77
"type": "git",
88
"url": "http://github.com/mrodrig/doc-path.git"
@@ -37,7 +37,7 @@
3737
"should": "13.2.3"
3838
},
3939
"engines": {
40-
"node": ">=6.0"
40+
"node": ">=10"
4141
},
4242
"license": "MIT"
4343
}

Diff for: src/path.js

+19-8
Original file line numberDiff line numberDiff line change
@@ -30,29 +30,40 @@ function evaluatePath(document, keyPath) {
3030
}
3131

3232
function setPath(document, keyPath, value) {
33+
if (!document) {
34+
throw new Error('No document was provided.');
35+
} else if (!keyPath) {
36+
throw new Error('No keyPath was provided.');
37+
}
38+
39+
// If this is clearly a prototype pollution attempt, then refuse to modify the path
40+
if (keyPath.startsWith('__proto__') || keyPath.startsWith('constructor')) {
41+
return document;
42+
}
43+
44+
return _setPath(document, keyPath, value);
45+
}
46+
47+
function _setPath(document, keyPath, value) {
3348
if (!document) {
3449
throw new Error('No document was provided.');
3550
}
3651

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

39-
// if (currentKey === '__proto__' || currentKey === 'prototype' && Object.prototype.hasOwnProperty.call(document, currentKey)) {
40-
if (currentKey === '__proto__') {
41-
// Refuse to modify anything on __proto__, return the document
42-
return document;
43-
} else if (indexOfDot >= 0) {
54+
if (indexOfDot >= 0) {
4455
// If there is a '.' in the keyPath, recur on the subdoc and ...
4556
if (!document[currentKey] && Array.isArray(document)) {
4657
// If this is an array and there are multiple levels of keys to iterate over, recur.
47-
return document.forEach((doc) => setPath(doc, keyPath, value));
58+
return document.forEach((doc) => _setPath(doc, keyPath, value));
4859
} else if (!document[currentKey]) {
4960
// If the currentKey doesn't exist yet, populate it
5061
document[currentKey] = {};
5162
}
52-
setPath(document[currentKey], remainingKeyPath, value);
63+
_setPath(document[currentKey], remainingKeyPath, value);
5364
} else if (Array.isArray(document)) {
5465
// If this "document" is actually an array, then we can loop over each of the values and set the path
55-
return document.forEach((doc) => setPath(doc, remainingKeyPath, value));
66+
return document.forEach((doc) => _setPath(doc, remainingKeyPath, value));
5667
} else {
5768
// Otherwise, we can set the path directly
5869
document[keyPath] = value;

Diff for: test/tests.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,20 @@ describe('doc-path Module', function() {
218218

219219
it('should protect against prototype pollution via __proto__', (done) => {
220220
doc = {};
221-
path.setPath(doc, '__proto__.polluted', 'yes');
221+
path.setPath(doc, '__proto__.polluted', 'prototype-polluted');
222222
assert.equal(doc.__proto__.polluted, undefined);
223223
assert.equal(doc.polluted, undefined);
224224
assert.equal({}.polluted, undefined);
225225
done();
226226
});
227+
228+
it('should protect against prototype pollution via constructor', (done) => {
229+
doc = {};
230+
path.setPath(doc, 'constructor', 'prototype-polluted');
231+
assert.equal(doc.constructor, Object);
232+
path.setPath(doc, 'constructor.prototype.test', 'prototype-polluted');
233+
assert.equal(doc.test, undefined);
234+
done();
235+
});
227236
});
228237
});

0 commit comments

Comments
 (0)