Skip to content

Commit 1ea0ab1

Browse files
committed
More more complete fix for prototype pollution
vulnerability first addressed in #384
1 parent c7acb02 commit 1ea0ab1

File tree

3 files changed

+41
-4
lines changed

3 files changed

+41
-4
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

88

9+
## [6.2.3] - 2022-05-07
10+
11+
### Fixed
12+
13+
- More more complete fix for prototype pollution vulnerability first addressed
14+
in #384 (Marc-Aurèle Darche @madarche, Snyk Security team)
15+
16+
917
## [6.2.2] - 2022-03-27
1018

1119
### Fixed

packages/convict/src/main.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ const cloneDeep = require('lodash.clonedeep')
1111

1212
// Forbidden key paths, for protection against prototype pollution
1313
const FORBIDDEN_KEY_PATHS = [
14-
'__proto__',
15-
'this.constructor.prototype',
14+
'__proto__.',
15+
'this.constructor.prototype.',
1616
]
1717

1818
const ALLOWED_OPTION_STRICT = 'strict'
@@ -567,8 +567,9 @@ const convict = function convict(def, opts) {
567567
* exist, they will be initialized to empty objects
568568
*/
569569
set: function(k, v) {
570-
for (const path of FORBIDDEN_KEY_PATHS) {
571-
if (k.startsWith(`${path}.`)) {
570+
for (const forbidden_key_path of FORBIDDEN_KEY_PATHS) {
571+
if (k.startsWith(forbidden_key_path) ||
572+
k.includes(`.${forbidden_key_path}`)) {
572573
return this
573574
}
574575
}

packages/convict/test/prototype_pollution.test.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,20 @@ describe('Convict prototype pollution resistance', function() {
1414
config.set('__proto__.nested.polluted_proto_nested', 'Polluted!')
1515
expect({}).not.toHaveProperty('nested')
1616
expect({}).not.toHaveProperty('nested.polluted_proto_nested')
17+
18+
config.set('this.__proto__.polluted_proto_root', 'Polluted!')
19+
expect({}).not.toHaveProperty('polluted_proto_root')
20+
21+
config.set('this.__proto__.nested.polluted_proto_nested', 'Polluted!')
22+
expect({}).not.toHaveProperty('nested')
23+
expect({}).not.toHaveProperty('nested.polluted_proto_nested')
24+
25+
config.set('foo.__proto__.polluted_proto_root', 'Polluted!')
26+
expect({}).not.toHaveProperty('polluted_proto_root')
27+
28+
config.set('foo.__proto__.nested.polluted_proto_nested', 'Polluted!')
29+
expect({}).not.toHaveProperty('nested')
30+
expect({}).not.toHaveProperty('nested.polluted_proto_nested')
1731
})
1832

1933
test('against this.constructor.prototype', function() {
@@ -26,6 +40,20 @@ describe('Convict prototype pollution resistance', function() {
2640
config.set('this.constructor.prototype.nested.polluted_constructor_prototype_nested', 'Polluted!')
2741
expect({}).not.toHaveProperty('nested')
2842
expect({}).not.toHaveProperty('nested.polluted_constructor_prototype_nested')
43+
44+
config.set('this.this.constructor.prototype.polluted_constructor_prototype_root', 'Polluted!')
45+
expect({}).not.toHaveProperty('polluted_constructor_prototype_root')
46+
47+
config.set('this.this.constructor.prototype.nested.polluted_constructor_prototype_nested', 'Polluted!')
48+
expect({}).not.toHaveProperty('nested')
49+
expect({}).not.toHaveProperty('nested.polluted_constructor_prototype_nested')
50+
51+
config.set('foo.this.constructor.prototype.polluted_constructor_prototype_root', 'Polluted!')
52+
expect({}).not.toHaveProperty('polluted_constructor_prototype_root')
53+
54+
config.set('foo.this.constructor.prototype.nested.polluted_constructor_prototype_nested', 'Polluted!')
55+
expect({}).not.toHaveProperty('nested')
56+
expect({}).not.toHaveProperty('nested.polluted_constructor_prototype_nested')
2957
})
3058

3159
})

0 commit comments

Comments
 (0)