Skip to content

Commit f76cff5

Browse files
committed
guard for possible prototype polution
1 parent ee628c2 commit f76cff5

File tree

5 files changed

+188
-119
lines changed

5 files changed

+188
-119
lines changed

index.js

Lines changed: 80 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,22 @@ function isEmptyObject (val) {
4545
return Object.keys(val).length === 0
4646
}
4747

48+
const blacklist = ['__proto__', 'prototype', 'constructor']
49+
4850
function parsePath (path, sep) {
4951
if (path.indexOf('[') >= 0) {
5052
path = path.replace(/\[/g, '.').replace(/]/g, '')
5153
}
52-
return path.split(sep)
54+
55+
const parts = path.split(sep)
56+
57+
const check = parts.filter(part => blacklist.indexOf(part) === -1)
58+
59+
if (check.length !== parts.length) {
60+
throw Error('Refusing to update blacklisted property ' + path)
61+
}
62+
63+
return parts
5364
}
5465

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

8596
if (a.length > 0) {
86-
obj[k] = obj[k] ||
87-
(this.useArray && isIndex(a[0]) ? [] : {})
97+
obj[k] = obj[k] || (this.useArray && isIndex(a[0]) ? [] : {})
8898

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

103113
this._fill(a, obj[k], v, mod)
104114
} else {
105-
if (!this.override &&
106-
isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) {
115+
if (!this.override && isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) {
107116
if (!(isArrayOrObject(v) && isEmptyObject(v))) {
108117
throw new Error("Trying to redefine non-empty obj['" + k + "']")
109118
}
@@ -195,7 +204,7 @@ DotObject.prototype.pick = function (path, obj, remove, reindexArray) {
195204
for (i = 0; i < keys.length; i++) {
196205
key = parseKey(keys[i], obj)
197206
if (obj && typeof obj === 'object' && key in obj) {
198-
if (i === (keys.length - 1)) {
207+
if (i === keys.length - 1) {
199208
if (remove) {
200209
val = obj[key]
201210
if (reindexArray && Array.isArray(obj)) {
@@ -221,7 +230,9 @@ DotObject.prototype.pick = function (path, obj, remove, reindexArray) {
221230
}
222231
}
223232
if (remove && Array.isArray(obj)) {
224-
obj = obj.filter(function (n) { return n !== undefined })
233+
obj = obj.filter(function (n) {
234+
return n !== undefined
235+
})
225236
}
226237
return obj
227238
}
@@ -279,7 +290,9 @@ DotObject.prototype._cleanup = function (obj) {
279290
keys = this.cleanup[i].split('.')
280291
root = keys.splice(0, -1).join('.')
281292
ret = root ? this.pick(root, obj) : obj
282-
ret = ret[keys[0]].filter(function (v) { return v !== undefined })
293+
ret = ret[keys[0]].filter(function (v) {
294+
return v !== undefined
295+
})
283296
this.set(this.cleanup[i], ret, obj)
284297
}
285298
this.cleanup = []
@@ -336,13 +349,21 @@ DotObject.prototype.move = function (source, target, obj, mods, merge) {
336349
* @param {Function|Array} mods
337350
* @param {Boolean} merge
338351
*/
339-
DotObject.prototype.transfer = function (source, target, obj1, obj2, mods, merge) {
352+
DotObject.prototype.transfer = function (
353+
source,
354+
target,
355+
obj1,
356+
obj2,
357+
mods,
358+
merge
359+
) {
340360
if (typeof mods === 'function' || Array.isArray(mods)) {
341-
this.set(target,
342-
_process(
343-
this.pick(source, obj1, true),
344-
mods
345-
), obj2, merge)
361+
this.set(
362+
target,
363+
_process(this.pick(source, obj1, true), mods),
364+
obj2,
365+
merge
366+
)
346367
} else {
347368
merge = mods
348369
this.set(target, this.pick(source, obj1, true), obj2, merge)
@@ -367,16 +388,16 @@ DotObject.prototype.transfer = function (source, target, obj1, obj2, mods, merge
367388
*/
368389
DotObject.prototype.copy = function (source, target, obj1, obj2, mods, merge) {
369390
if (typeof mods === 'function' || Array.isArray(mods)) {
370-
this.set(target,
391+
this.set(
392+
target,
371393
_process(
372394
// clone what is picked
373-
JSON.parse(
374-
JSON.stringify(
375-
this.pick(source, obj1, false)
376-
)
377-
),
395+
JSON.parse(JSON.stringify(this.pick(source, obj1, false))),
378396
mods
379-
), obj2, merge)
397+
),
398+
obj2,
399+
merge
400+
)
380401
} else {
381402
merge = mods
382403
this.set(target, this.pick(source, obj1, false), obj2, merge)
@@ -408,7 +429,7 @@ DotObject.prototype.set = function (path, val, obj, merge) {
408429

409430
for (i = 0; i < keys.length; i++) {
410431
key = keys[i]
411-
if (i === (keys.length - 1)) {
432+
if (i === keys.length - 1) {
412433
if (merge && isObject(val) && isObject(obj[key])) {
413434
for (k in val) {
414435
if (hasOwnProperty.call(val, k)) {
@@ -447,14 +468,14 @@ DotObject.prototype.set = function (path, val, obj, merge) {
447468
*
448469
* var obj = {
449470
* "id": 1,
450-
* "some": {
451-
* "thing": "else"
452-
* }
471+
* "some": {
472+
* "thing": "else"
473+
* }
453474
* }
454475
*
455476
* var transform = {
456477
* "id": "nr",
457-
* "some.thing": "name"
478+
* "some.thing": "name"
458479
* }
459480
*
460481
* var tgt = dot.transform(transform, obj)
@@ -466,9 +487,11 @@ DotObject.prototype.set = function (path, val, obj, merge) {
466487
DotObject.prototype.transform = function (recipe, obj, tgt) {
467488
obj = obj || {}
468489
tgt = tgt || {}
469-
Object.keys(recipe).forEach(function (key) {
470-
this.set(recipe[key], this.pick(key, obj), tgt)
471-
}.bind(this))
490+
Object.keys(recipe).forEach(
491+
function (key) {
492+
this.set(recipe[key], this.pick(key, obj), tgt)
493+
}.bind(this)
494+
)
472495
return tgt
473496
}
474497

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

497-
Object.keys(obj).forEach(function (key) {
498-
var index = isArray && this.useBrackets ? '[' + key + ']' : key
499-
if (
500-
(
520+
Object.keys(obj).forEach(
521+
function (key) {
522+
var index = isArray && this.useBrackets ? '[' + key + ']' : key
523+
if (
501524
isArrayOrObject(obj[key]) &&
502-
(
503-
(isObject(obj[key]) && !isEmptyObject(obj[key])) ||
504-
(Array.isArray(obj[key]) && (!this.keepArray && (obj[key].length !== 0)))
505-
)
506-
)
507-
) {
508-
if (isArray && this.useBrackets) {
509-
var previousKey = path[path.length - 1] || ''
510-
return this.dot(obj[key], tgt, path.slice(0, -1).concat(previousKey + index))
511-
} else {
512-
return this.dot(obj[key], tgt, path.concat(index))
513-
}
514-
} else {
515-
if (isArray && this.useBrackets) {
516-
tgt[path.join(this.separator).concat('[' + key + ']')] = obj[key]
525+
((isObject(obj[key]) && !isEmptyObject(obj[key])) ||
526+
(Array.isArray(obj[key]) && !this.keepArray && obj[key].length !== 0))
527+
) {
528+
if (isArray && this.useBrackets) {
529+
var previousKey = path[path.length - 1] || ''
530+
return this.dot(
531+
obj[key],
532+
tgt,
533+
path.slice(0, -1).concat(previousKey + index)
534+
)
535+
} else {
536+
return this.dot(obj[key], tgt, path.concat(index))
537+
}
517538
} else {
518-
tgt[path.concat(index).join(this.separator)] = obj[key]
539+
if (isArray && this.useBrackets) {
540+
tgt[path.join(this.separator).concat('[' + key + ']')] = obj[key]
541+
} else {
542+
tgt[path.concat(index).join(this.separator)] = obj[key]
543+
}
519544
}
520-
}
521-
}.bind(this))
545+
}.bind(this)
546+
)
522547
return tgt
523548
}
524549

@@ -532,9 +557,8 @@ DotObject.str = wrap('str')
532557
DotObject.set = wrap('set')
533558
DotObject.delete = wrap('delete')
534559
DotObject.del = DotObject.remove = wrap('remove')
535-
DotObject.dot = wrap('dot')
536-
537-
;['override', 'overwrite'].forEach(function (prop) {
560+
DotObject.dot = wrap('dot');
561+
['override', 'overwrite'].forEach(function (prop) {
538562
Object.defineProperty(DotObject, prop, {
539563
get: function () {
540564
return dotDefault.override
@@ -543,9 +567,8 @@ DotObject.dot = wrap('dot')
543567
dotDefault.override = !!val
544568
}
545569
})
546-
})
547-
548-
;['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
570+
});
571+
['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
549572
Object.defineProperty(DotObject, prop, {
550573
get: function () {
551574
return dotDefault[prop]

0 commit comments

Comments
 (0)