Skip to content

Commit

Permalink
fix: prototype pollution vulnerability + working tests
Browse files Browse the repository at this point in the history
  • Loading branch information
deleonio committed Nov 12, 2020
1 parent 49031e4 commit 7721efe
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 5 deletions.
19 changes: 14 additions & 5 deletions index.js
@@ -1,6 +1,5 @@
'use strict';


/* !
* Chai - pathval utility
* Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com>
Expand Down Expand Up @@ -77,6 +76,13 @@ function parsePath(path) {
var str = path.replace(/([^\\])\[/g, '$1.[');
var parts = str.match(/(\\\.|[^.]+?)+/g);
return parts.map(function mapMatches(value) {
if (
value === 'constructor' ||
value === '__proto__' ||
value === 'prototype'
) {
return {};
}
var regexp = /^\[(\d+)\]$/;
var mArr = regexp.exec(value);
var parsed = null;
Expand Down Expand Up @@ -108,7 +114,7 @@ function parsePath(path) {
function internalGetPathValue(obj, parsed, pathDepth) {
var temporaryValue = obj;
var res = null;
pathDepth = (typeof pathDepth === 'undefined' ? parsed.length : pathDepth);
pathDepth = typeof pathDepth === 'undefined' ? parsed.length : pathDepth;

for (var i = 0; i < pathDepth; i++) {
var part = parsed[i];
Expand All @@ -119,7 +125,7 @@ function internalGetPathValue(obj, parsed, pathDepth) {
temporaryValue = temporaryValue[part.p];
}

if (i === (pathDepth - 1)) {
if (i === pathDepth - 1) {
res = temporaryValue;
}
}
Expand Down Expand Up @@ -153,7 +159,7 @@ function internalSetPathValue(obj, val, parsed) {
part = parsed[i];

// If it's the last part of the path, we set the 'propName' value with the property name
if (i === (pathDepth - 1)) {
if (i === pathDepth - 1) {
propName = typeof part.p === 'undefined' ? part.i : part.p;
// Now we set the property with the name held by 'propName' on object with the desired val
tempObj[propName] = val;
Expand Down Expand Up @@ -200,7 +206,10 @@ function getPathInfo(obj, path) {
var parsed = parsePath(path);
var last = parsed[parsed.length - 1];
var info = {
parent: parsed.length > 1 ? internalGetPathValue(obj, parsed, parsed.length - 1) : obj,
parent:
parsed.length > 1 ?
internalGetPathValue(obj, parsed, parsed.length - 1) :
obj,
name: last.p || last.i,
value: internalGetPathValue(obj, parsed),
};
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -21,6 +21,7 @@
"scripts": {
"build": "browserify --standalone pathval -o pathval.js",
"lint": "eslint --ignore-path .gitignore .",
"lint:fix": "npm run lint -- --fix",
"prepublish": "npm run build",
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
"pretest": "npm run lint",
Expand Down
26 changes: 26 additions & 0 deletions test/index.js
Expand Up @@ -218,4 +218,30 @@ describe('setPathValue', function () {
var valueReturned = pathval.setPathValue(obj, 'hello[2]', 3);
assert(obj === valueReturned);
});

describe('fix prototype pollution vulnerability', () => {

it('exclude constructor', function () {
const obj = {};
assert(typeof obj.constructor === 'function'); // eslint-disable-line
pathval.setPathValue(obj, 'constructor', null);
assert(typeof obj.constructor === 'function'); // eslint-disable-line
});

it('exclude __proto__', function () {
const obj = {};
assert(typeof polluted === 'undefined'); // eslint-disable-line
pathval.setPathValue(obj, '__proto__.polluted', true);
assert(typeof polluted === 'undefined'); // eslint-disable-line
});

it('exclude prototype', function () {
const obj = {};
assert(typeof obj.prototype === 'undefined'); // eslint-disable-line
pathval.setPathValue(obj, 'prototype', true);
assert(typeof obj.prototype === 'undefined'); // eslint-disable-line
});

});

});

0 comments on commit 7721efe

Please sign in to comment.