CVE-2020-7600: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') (CWE-1321)
value: querymen prior to 2.1.4 allows modification of object properties. The parameters of exported function handler(type, name, fn) can be controlled by users without any sanitization. This could be abused for Prototype Pollution attacks.
CWE description: The product receives input from an upstream component that specifies attributes that are to be initialized or updated in an object, but it does not properly control modifications of attributes of the object prototype.
CVSS v3 base score: 5.3
Published: 2020-03-12T23:15Z
Repository: https://github.com/diegohaz/querymen
Language: JavaScript
Files changed: .travis.yml, src/index.js, test/index.js, test/querymen-schema.js
Commit date: 2020-03-12 14:17:17 -0300
Commit message: Fix prototype pollution vulnerability (#77)

* Fix prototype pollution vulnerability

* Fix test
Diff: diff --git a/.travis.yml b/.travis.yml
index ddf8371..658ac06 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,6 @@
 language: node_js
 services: mongodb
 node_js:
-  - v5
-  - v4
+  - v6
 after_script:
   - 'npm run coveralls'
diff --git a/src/index.js b/src/index.js
index de520de..7673d0b 100644
--- a/src/index.js
+++ b/src/index.js
@@ -17,10 +17,18 @@ export let handlers = {
  * @param {string} type - Handler type.
  * @param {string} name - Handler name.
  * @param {Function} [fn] - Set the handler method.
  */
 export function handler (type, name, fn) {
+  if (
+    type === 'constructor' ||
+    type === '__proto__' ||
+    name === 'constructor' ||
+    name === '__proto__'
+  ) {
+    return
+  }
   if (arguments.length > 2) {
     handlers[type][name] = fn
   }
 
   return handlers[type][name]
diff --git a/test/index.js b/test/index.js
index 04ee025..bb9cf01 100644
--- a/test/index.js
+++ b/test/index.js
@@ -40,10 +40,25 @@ const route = (...args) => {
 
   app.use(querymen.errorHandler())
   return app
 }
 
+test('Prototype pollution', (t) => {
+  const { toString } = {}
+
+  querymen.handler('__proto__', 'toString', 'JHU')
+  t.ok({}.toString === toString, 'should not be vulnerable to prototype pollution')
+
+  querymen.handler('formatters', '__proto__', { toString: 'JHU' })
+  t.ok({}.toString === toString, 'should not be vulnerable to prototype pollution')
+
+  querymen.handler('validators', '__proto__', { toString: 'JHU' })
+  t.ok({}.toString === toString, 'should not be vulnerable to prototype pollution')
+
+  t.end()
+})
+
 test('Querymen handler', (t) => {
   t.notOk(querymen.parser('testParser'), 'should not get nonexistent parser')
   t.notOk(querymen.formatter('testFormatter'), 'should not get nonexistent formatter')
   t.notOk(querymen.validator('testValidator'), 'should not get nonexistent validator')
 
diff --git a/test/querymen-schema.js b/test/querymen-schema.js
index b2e92a3..11ef39a 100644
--- a/test/querymen-schema.js
+++ b/test/querymen-schema.js
@@ -27,11 +27,11 @@ test('QuerymenSchema add', (t) => {
   t.same(add(null, [/123/i]), /123/i, 'should add a param with default option regexp array')
   t.same(add(123, [String]), '123', 'should add a param with type option string array')
   t.same(add('123,456', [Number]), [123, 456], 'should add a param with type option number array')
   t.same(add('123,0', [Boolean]), [true, false], 'should add a param with type option boolean array')
   t.same(add('2016,2017', [Date]), [new Date('2016'), new Date('2017')], 'should add a param with type option date array')
-  t.same(add('123,456', [RegExp]), [/123/i, /123/i], 'should add a param with type option regexp array')
+  t.same(add('123,456', [RegExp]), [/123/i, /456/i], 'should add a param with type option regexp array')
   t.end()
 })
 
 test('QuerymenSchema get', (t) => {
   let mySchema = schema()

Vulnerable code:   - v5
  - v4
  t.same(add('123,456', [RegExp]), [/123/i, /123/i], 'should add a param with type option regexp array')
Fixed code:   - v6
  if (
    type === 'constructor' ||
    type === '__proto__' ||
    name === 'constructor' ||
    name === '__proto__'
  ) {
    return
  }
test('Prototype pollution', (t) => {
  const { toString } = {}

  querymen.handler('__proto__', 'toString', 'JHU')
  t.ok({}.toString === toString, 'should not be vulnerable to prototype pollution')

  querymen.handler('formatters', '__proto__', { toString: 'JHU' })
  t.ok({}.toString === toString, 'should not be vulnerable to prototype pollution')

  querymen.handler('validators', '__proto__', { toString: 'JHU' })
  t.ok({}.toString === toString, 'should not be vulnerable to prototype pollution')

  t.end()
})

  t.same(add('123,456', [RegExp]), [/123/i, /456/i], 'should add a param with type option regexp array')