CVE-2020-7677 (JavaScript, published 2022-07-25T14:15Z)
Description: value: 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.
Severity: nan (CVSS v3 base score: 9.8)
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/thenables/thenify
Files changed:
index.js
test/test.js
Commit message: fix: remove eval (#30)



Vulnerable code:
 * @param {Function} $$__fn__$$
function thenify($$__fn__$$, options) {
  assert(typeof $$__fn__$$ === 'function')
  return eval(createWrapper($$__fn__$$.name, options))
 * @param {Function} $$__fn__$$
thenify.withCallback = function ($$__fn__$$, options) {
  assert(typeof $$__fn__$$ === 'function')
  if (options.multiArgs === undefined) options.multiArgs = true
  return eval(createWrapper($$__fn__$$.name, options))
function createWrapper(name, options) {
  name = (name || '').replace(/\s|bound(?!$)/g, '')
  // 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'
  + '})'

Fixed code:
 * @param {Function} fn
function thenify(fn, options) {
  assert(typeof fn === 'function')
  return createWrapper(fn, options)
 * @param {Function} fn
thenify.withCallback = function (fn, options) {
  assert(typeof fn === 'function')
  return createWrapper(fn, options)
  // default to true
  if (multiArgs === undefined) multiArgs = true
function createWrapper(fn, options) {
  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

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])
  })
})