Skip to content

Commit df84691

Browse files
committed
fix: prototype polution vector
1 parent 8459582 commit df84691

File tree

3 files changed

+58
-20
lines changed

3 files changed

+58
-20
lines changed

compiler.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ function makeSafe(path, param) {
88
parts = split(path),
99
isLast
1010

11-
forEach(parts, function(part, isBracket, isArray, idx, parts) {
11+
forEach(parts, function (part, isBracket, isArray, idx, parts) {
1212
isLast = idx === parts.length - 1
1313

1414
part = isBracket || isArray ? '[' + part + ']' : '.' + part
@@ -36,7 +36,15 @@ function expr(expression, safe, param) {
3636

3737
module.exports = {
3838
expr,
39-
setter: function(path) {
39+
setter: function (path) {
40+
if (
41+
path.indexOf('__proto__') !== -1 ||
42+
path.indexOf('constructor') !== -1 ||
43+
path.indexOf('prototype') !== -1
44+
) {
45+
return (obj) => obj
46+
}
47+
4048
return (
4149
setCache.get(path) ||
4250
setCache.set(
@@ -46,7 +54,7 @@ module.exports = {
4654
)
4755
},
4856

49-
getter: function(path, safe) {
57+
getter: function (path, safe) {
5058
var key = path + '_' + safe
5159
return (
5260
getCache.get(key) ||
@@ -55,5 +63,5 @@ module.exports = {
5563
new Function('data', 'return ' + expr(path, safe, 'data'))
5664
)
5765
)
58-
}
66+
},
5967
}

index.js

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ function Cache(maxSize) {
77
this._maxSize = maxSize
88
this.clear()
99
}
10-
Cache.prototype.clear = function() {
10+
Cache.prototype.clear = function () {
1111
this._size = 0
1212
this._values = Object.create(null)
1313
}
14-
Cache.prototype.get = function(key) {
14+
Cache.prototype.get = function (key) {
1515
return this._values[key]
1616
}
17-
Cache.prototype.set = function(key, value) {
17+
Cache.prototype.set = function (key, value) {
1818
this._size >= this._maxSize && this.clear()
1919
if (!(key in this._values)) this._size++
2020

@@ -41,23 +41,34 @@ module.exports = {
4141

4242
normalizePath: normalizePath,
4343

44-
setter: function(path) {
44+
setter: function (path) {
4545
var parts = normalizePath(path)
4646

4747
return (
4848
setCache.get(path) ||
49-
setCache.set(path, function setter(data, value) {
50-
var index = 0,
51-
len = parts.length
49+
setCache.set(path, function setter(obj, value) {
50+
var index = 0
51+
var len = parts.length
52+
var data = obj
53+
5254
while (index < len - 1) {
55+
let part = parts[index]
56+
if (
57+
part === '__proto__' ||
58+
part === 'constructor' ||
59+
part === 'prototype'
60+
) {
61+
return obj
62+
}
63+
5364
data = data[parts[index++]]
5465
}
5566
data[parts[index]] = value
5667
})
5768
)
5869
},
5970

60-
getter: function(path, safe) {
71+
getter: function (path, safe) {
6172
var parts = normalizePath(path)
6273
return (
6374
getCache.get(path) ||
@@ -73,8 +84,8 @@ module.exports = {
7384
)
7485
},
7586

76-
join: function(segments) {
77-
return segments.reduce(function(path, part) {
87+
join: function (segments) {
88+
return segments.reduce(function (path, part) {
7889
return (
7990
path +
8091
(isQuoted(part) || DIGIT_REGEX.test(part)
@@ -84,17 +95,17 @@ module.exports = {
8495
}, '')
8596
},
8697

87-
forEach: function(path, cb, thisArg) {
98+
forEach: function (path, cb, thisArg) {
8899
forEach(Array.isArray(path) ? path : split(path), cb, thisArg)
89-
}
100+
},
90101
}
91102

92103
function normalizePath(path) {
93104
return (
94105
pathCache.get(path) ||
95106
pathCache.set(
96107
path,
97-
split(path).map(function(part) {
108+
split(path).map(function (part) {
98109
return part.replace(CLEAN_QUOTES_REGEX, '$2')
99110
})
100111
)

test.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ const a = require('assert')
22
const expr = require('./index')
33
const compiler = require('./compiler')
44

5+
const root = (typeof global == 'object' && global) || this
6+
57
function runSetterGetterTests({ setter, getter }) {
68
let obj = {
79
foo: {
810
bar: ['baz', 'bux'],
911
fux: 5,
1012
'00N40000002S5U0': 1,
1113
N40000002S5U0: 2,
12-
'FE43-D880-21AE': 3
13-
}
14+
'FE43-D880-21AE': 3,
15+
},
1416
}
1517

1618
// -- Getters --
@@ -47,7 +49,24 @@ function runSetterGetterTests({ setter, getter }) {
4749
a.strictEqual(obj.foo.bar[1], 'bot')
4850

4951
setter('[\'foo\']["bar"][1]')(obj, 'baz')
52+
5053
a.strictEqual(obj.foo.bar[1], 'baz')
54+
//
55+
;['__proto__', 'constructor', 'prototype'].forEach((keyToTest) => {
56+
setter(`${keyToTest}.a`)({}, 'newValue')
57+
58+
a.notEqual(root['a'], 'newValue')
59+
60+
const b = 'oldValue'
61+
62+
a.equal(b, 'oldValue')
63+
a.notEqual(root['b'], 'newValue')
64+
65+
setter(`${keyToTest}.b`)({}, 'newValue')
66+
a.equal(b, 'oldValue')
67+
a.notEqual(root['b'], 'newValue')
68+
a.equal(root['b'], undefined)
69+
})
5170
}
5271

5372
console.log('--- Test Start ---')
@@ -89,7 +108,7 @@ a.strictEqual(expr.join(parts), 'foo.baz["bar"][1]')
89108

90109
let count = 0
91110

92-
expr.forEach('foo.baz["bar"][1]', function(part, isBracket, isArray, idx) {
111+
expr.forEach('foo.baz["bar"][1]', function (part, isBracket, isArray, idx) {
93112
count = idx
94113

95114
switch (idx) {

0 commit comments

Comments
 (0)