VYPR
Critical severityNVD Advisory· Published Jul 25, 2022· Updated Sep 16, 2024

Arbitrary Code Execution

CVE-2020-7677

Description

This affects the package thenify before 3.3.1. The name argument provided to the package can be controlled by users without any sanitization, and this is provided to the eval function without any sanitization.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
thenifynpm
< 3.3.13.3.1
org.webjars.npm:thenifyMaven
< 3.3.13.3.1

Affected products

1

Patches

1
0d94a24eb933

fix: remove eval (#30)

https://github.com/thenables/thenifyYiyu HeJun 17, 2020via ghsa
2 files changed · +54 33
  • index.js+30 33 modified
    @@ -7,33 +7,34 @@ module.exports = thenify
     /**
      * Turn async functions into promises
      *
    - * @param {Function} $$__fn__$$
    + * @param {Function} fn
      * @return {Function}
      * @api public
      */
     
    -function thenify($$__fn__$$, options) {
    -  assert(typeof $$__fn__$$ === 'function')
    -  return eval(createWrapper($$__fn__$$.name, options))
    +function thenify(fn, options) {
    +  assert(typeof fn === 'function')
    +  return createWrapper(fn, options)
     }
     
     /**
      * Turn async functions into promises and backward compatible with callback
      *
    - * @param {Function} $$__fn__$$
    + * @param {Function} fn
      * @return {Function}
      * @api public
      */
     
    -thenify.withCallback = function ($$__fn__$$, options) {
    -  assert(typeof $$__fn__$$ === 'function')
    +thenify.withCallback = function (fn, options) {
    +  assert(typeof fn === 'function')
       options = options || {}
       options.withCallback = true
    -  if (options.multiArgs === undefined) options.multiArgs = true
    -  return eval(createWrapper($$__fn__$$.name, options))
    +  return createWrapper(fn, options)
     }
     
     function createCallback(resolve, reject, multiArgs) {
    +  // default to true
    +  if (multiArgs === undefined) multiArgs = true
       return function(err, value) {
         if (err) return reject(err)
         var length = arguments.length
    @@ -52,29 +53,25 @@ function createCallback(resolve, reject, multiArgs) {
       }
     }
     
    -function createWrapper(name, options) {
    -  name = (name || '').replace(/\s|bound(?!$)/g, '')
    +function createWrapper(fn, options) {
       options = options || {}
    -  // default to true
    -  var multiArgs = options.multiArgs !== undefined ? options.multiArgs : true
    -  multiArgs = 'var multiArgs = ' + JSON.stringify(multiArgs) + '\n'
    -
    -  var withCallback = options.withCallback ?
    -    'var lastType = typeof arguments[len - 1]\n'
    -    + 'if (lastType === "function") return $$__fn__$$.apply(self, arguments)\n'
    -   : ''
    -
    -  return '(function ' + name + '() {\n'
    -    + 'var self = this\n'
    -    + 'var len = arguments.length\n'
    -    + multiArgs
    -    + withCallback
    -    + 'var args = new Array(len + 1)\n'
    -    + 'for (var i = 0; i < len; ++i) args[i] = arguments[i]\n'
    -    + 'var lastIndex = i\n'
    -    + 'return new Promise(function (resolve, reject) {\n'
    -      + 'args[lastIndex] = createCallback(resolve, reject, multiArgs)\n'
    -      + '$$__fn__$$.apply(self, args)\n'
    -    + '})\n'
    -  + '})'
    +  var name = fn.name;
    +  name = (name || '').replace(/\s|bound(?!$)/g, '')
    +  var newFn = function () {
    +    var self = this
    +    var len = arguments.length
    +    if (options.withCallback) {
    +      var lastType = typeof arguments[len - 1]
    +      if (lastType === 'function') return fn.apply(self, arguments)
    +    }
    +    var args = new Array(len + 1)
    +    for (var i = 0; i < len; ++i) args[i] = arguments[i]
    +    var lastIndex = i
    +    return new Promise(function (resolve, reject) {
    +      args[lastIndex] = createCallback(resolve, reject, options.multiArgs)
    +      fn.apply(self, args)
    +    })
    +  }
    +  Object.defineProperty(newFn, 'name', { value: name })
    +  return newFn
     }
    
  • test/test.js+24 0 modified
    @@ -69,3 +69,27 @@ it('fn(..args, callback())', function () {
         assert.deepEqual(values, [1, 2, 3])
       })
     })
    +
    +it('unicode function name', function () {
    +  function 你好$hello_123(a, b, c, cb) {
    +    cb(null, a, b, c)
    +  }
    +  var wrapper = thenify(你好$hello_123)
    +  assert.equal(wrapper.name, '你好$hello_123')
    +  wrapper(1, 2, 3).then(function (values) {
    +    assert.deepEqual(values, [1, 2, 3])
    +  })
    +})
    +
    +it('invalid function name', function () {
    +  function fn(a, b, c, cb) {
    +    cb(null, a, b, c)
    +  }
    +
    +  Object.defineProperty(fn, 'name', { value: 'fake(){a.b;})();(function(){//' })
    +  var wrapper = thenify(fn)
    +  assert.equal(wrapper.name, fn.name)
    +  wrapper(1, 2, 3).then(function (values) {
    +    assert.deepEqual(values, [1, 2, 3])
    +  })
    +})
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

12

News mentions

0

No linked articles in our index yet.