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.
| Package | Affected versions | Patched versions |
|---|---|---|
thenifynpm | < 3.3.1 | 3.3.1 |
org.webjars.npm:thenifyMaven | < 3.3.1 | 3.3.1 |
Affected products
1Patches
12 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- github.com/advisories/GHSA-29xr-v42j-r956ghsaADVISORY
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/MTEUUTNIEBHGKUKKLNUZSV7IEP6IP3Q3/mitrevendor-advisory
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/UM6XJ73Q3NAM5KSGCOKJ2ZIA6GUWUJLK/mitrevendor-advisory
- nvd.nist.gov/vuln/detail/CVE-2020-7677ghsaADVISORY
- github.com/thenables/thenify/blob/master/index.js%23L17ghsaWEB
- github.com/thenables/thenify/commit/0d94a24eb933bc835d568f3009f4d269c4c4c17aghsaWEB
- github.com/thenables/thenify/issues/29ghsaWEB
- lists.debian.org/debian-lts-announce/2022/09/msg00039.htmlghsamailing-listWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/MTEUUTNIEBHGKUKKLNUZSV7IEP6IP3Q3ghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UM6XJ73Q3NAM5KSGCOKJ2ZIA6GUWUJLKghsaWEB
- security.snyk.io/vuln/SNYK-JAVA-ORGWEBJARSNPM-572317ghsaWEB
- security.snyk.io/vuln/SNYK-JS-THENIFY-571690ghsaWEB
News mentions
0No linked articles in our index yet.