Skip to content

Commit

Permalink
guard for possible prototype polution
Browse files Browse the repository at this point in the history
  • Loading branch information
rhalff committed Feb 16, 2020
1 parent ee628c2 commit f76cff5
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 119 deletions.
137 changes: 80 additions & 57 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,22 @@ function isEmptyObject (val) {
return Object.keys(val).length === 0
}

const blacklist = ['__proto__', 'prototype', 'constructor']

function parsePath (path, sep) {
if (path.indexOf('[') >= 0) {
path = path.replace(/\[/g, '.').replace(/]/g, '')
}
return path.split(sep)

const parts = path.split(sep)

const check = parts.filter(part => blacklist.indexOf(part) === -1)

if (check.length !== parts.length) {
throw Error('Refusing to update blacklisted property ' + path)
}

return parts
}

var hasOwnProperty = Object.prototype.hasOwnProperty
Expand Down Expand Up @@ -83,8 +94,7 @@ DotObject.prototype._fill = function (a, obj, v, mod) {
var k = a.shift()

if (a.length > 0) {
obj[k] = obj[k] ||
(this.useArray && isIndex(a[0]) ? [] : {})
obj[k] = obj[k] || (this.useArray && isIndex(a[0]) ? [] : {})

if (!isArrayOrObject(obj[k])) {
if (this.override) {
Expand All @@ -102,8 +112,7 @@ DotObject.prototype._fill = function (a, obj, v, mod) {

this._fill(a, obj[k], v, mod)
} else {
if (!this.override &&
isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) {
if (!this.override && isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) {
if (!(isArrayOrObject(v) && isEmptyObject(v))) {
throw new Error("Trying to redefine non-empty obj['" + k + "']")
}
Expand Down Expand Up @@ -195,7 +204,7 @@ DotObject.prototype.pick = function (path, obj, remove, reindexArray) {
for (i = 0; i < keys.length; i++) {
key = parseKey(keys[i], obj)
if (obj && typeof obj === 'object' && key in obj) {
if (i === (keys.length - 1)) {
if (i === keys.length - 1) {
if (remove) {
val = obj[key]
if (reindexArray && Array.isArray(obj)) {
Expand All @@ -221,7 +230,9 @@ DotObject.prototype.pick = function (path, obj, remove, reindexArray) {
}
}
if (remove && Array.isArray(obj)) {
obj = obj.filter(function (n) { return n !== undefined })
obj = obj.filter(function (n) {
return n !== undefined
})
}
return obj
}
Expand Down Expand Up @@ -279,7 +290,9 @@ DotObject.prototype._cleanup = function (obj) {
keys = this.cleanup[i].split('.')
root = keys.splice(0, -1).join('.')
ret = root ? this.pick(root, obj) : obj
ret = ret[keys[0]].filter(function (v) { return v !== undefined })
ret = ret[keys[0]].filter(function (v) {
return v !== undefined
})
this.set(this.cleanup[i], ret, obj)
}
this.cleanup = []
Expand Down Expand Up @@ -336,13 +349,21 @@ DotObject.prototype.move = function (source, target, obj, mods, merge) {
* @param {Function|Array} mods
* @param {Boolean} merge
*/
DotObject.prototype.transfer = function (source, target, obj1, obj2, mods, merge) {
DotObject.prototype.transfer = function (
source,
target,
obj1,
obj2,
mods,
merge
) {
if (typeof mods === 'function' || Array.isArray(mods)) {
this.set(target,
_process(
this.pick(source, obj1, true),
mods
), obj2, merge)
this.set(
target,
_process(this.pick(source, obj1, true), mods),
obj2,
merge
)
} else {
merge = mods
this.set(target, this.pick(source, obj1, true), obj2, merge)
Expand All @@ -367,16 +388,16 @@ DotObject.prototype.transfer = function (source, target, obj1, obj2, mods, merge
*/
DotObject.prototype.copy = function (source, target, obj1, obj2, mods, merge) {
if (typeof mods === 'function' || Array.isArray(mods)) {
this.set(target,
this.set(
target,
_process(
// clone what is picked
JSON.parse(
JSON.stringify(
this.pick(source, obj1, false)
)
),
JSON.parse(JSON.stringify(this.pick(source, obj1, false))),
mods
), obj2, merge)
),
obj2,
merge
)
} else {
merge = mods
this.set(target, this.pick(source, obj1, false), obj2, merge)
Expand Down Expand Up @@ -408,7 +429,7 @@ DotObject.prototype.set = function (path, val, obj, merge) {

for (i = 0; i < keys.length; i++) {
key = keys[i]
if (i === (keys.length - 1)) {
if (i === keys.length - 1) {
if (merge && isObject(val) && isObject(obj[key])) {
for (k in val) {
if (hasOwnProperty.call(val, k)) {
Expand Down Expand Up @@ -447,14 +468,14 @@ DotObject.prototype.set = function (path, val, obj, merge) {
*
* var obj = {
* "id": 1,
* "some": {
* "thing": "else"
* }
* "some": {
* "thing": "else"
* }
* }
*
* var transform = {
* "id": "nr",
* "some.thing": "name"
* "some.thing": "name"
* }
*
* var tgt = dot.transform(transform, obj)
Expand All @@ -466,9 +487,11 @@ DotObject.prototype.set = function (path, val, obj, merge) {
DotObject.prototype.transform = function (recipe, obj, tgt) {
obj = obj || {}
tgt = tgt || {}
Object.keys(recipe).forEach(function (key) {
this.set(recipe[key], this.pick(key, obj), tgt)
}.bind(this))
Object.keys(recipe).forEach(
function (key) {
this.set(recipe[key], this.pick(key, obj), tgt)
}.bind(this)
)
return tgt
}

Expand All @@ -494,31 +517,33 @@ DotObject.prototype.dot = function (obj, tgt, path) {
path = path || []
var isArray = Array.isArray(obj)

Object.keys(obj).forEach(function (key) {
var index = isArray && this.useBrackets ? '[' + key + ']' : key
if (
(
Object.keys(obj).forEach(
function (key) {
var index = isArray && this.useBrackets ? '[' + key + ']' : key
if (
isArrayOrObject(obj[key]) &&
(
(isObject(obj[key]) && !isEmptyObject(obj[key])) ||
(Array.isArray(obj[key]) && (!this.keepArray && (obj[key].length !== 0)))
)
)
) {
if (isArray && this.useBrackets) {
var previousKey = path[path.length - 1] || ''
return this.dot(obj[key], tgt, path.slice(0, -1).concat(previousKey + index))
} else {
return this.dot(obj[key], tgt, path.concat(index))
}
} else {
if (isArray && this.useBrackets) {
tgt[path.join(this.separator).concat('[' + key + ']')] = obj[key]
((isObject(obj[key]) && !isEmptyObject(obj[key])) ||
(Array.isArray(obj[key]) && !this.keepArray && obj[key].length !== 0))
) {
if (isArray && this.useBrackets) {
var previousKey = path[path.length - 1] || ''
return this.dot(
obj[key],
tgt,
path.slice(0, -1).concat(previousKey + index)
)
} else {
return this.dot(obj[key], tgt, path.concat(index))
}
} else {
tgt[path.concat(index).join(this.separator)] = obj[key]
if (isArray && this.useBrackets) {
tgt[path.join(this.separator).concat('[' + key + ']')] = obj[key]
} else {
tgt[path.concat(index).join(this.separator)] = obj[key]
}
}
}
}.bind(this))
}.bind(this)
)
return tgt
}

Expand All @@ -532,9 +557,8 @@ DotObject.str = wrap('str')
DotObject.set = wrap('set')
DotObject.delete = wrap('delete')
DotObject.del = DotObject.remove = wrap('remove')
DotObject.dot = wrap('dot')

;['override', 'overwrite'].forEach(function (prop) {
DotObject.dot = wrap('dot');
['override', 'overwrite'].forEach(function (prop) {
Object.defineProperty(DotObject, prop, {
get: function () {
return dotDefault.override
Expand All @@ -543,9 +567,8 @@ DotObject.dot = wrap('dot')
dotDefault.override = !!val
}
})
})

;['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
});
['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
Object.defineProperty(DotObject, prop, {
get: function () {
return dotDefault[prop]
Expand Down
Loading

0 comments on commit f76cff5

Please sign in to comment.