Critical severity9.8NVD Advisory· Published Feb 10, 2017· Updated May 13, 2026
CVE-2017-5954
CVE-2017-5954
Description
An issue was discovered in the serialize-to-js package 0.5.0 for Node.js. Untrusted data passed into the deserialize() function can be exploited to achieve arbitrary code execution by passing a JavaScript Object with an Immediately Invoked Function Expression (IIFE).
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
serialize-to-jsnpm | < 1.0.0 | 1.0.0 |
Affected products
1- cpe:2.3:a:serialize-to-js_project:serialize-to-js:0.5.0:*:*:*:*:node.js:*:*
Patches
11cd433960e5bharmful deserialization fix
8 files changed · +110 −20
lib/deserialize.js+11 −5 modified@@ -6,25 +6,31 @@ 'use strict' +var sanitize = require('./internal/sanitize') + /** * deserialize a serialized object to javascript * - * #### Example - serializing regex, date, buffer, ... + * _NOTE_: Deserialization uses `new Function()` for code evaluation which may be "harmful". + * In default mode input code gets inspected, but removing `new Function, function, eval` might still not be sufficient. + * *So now you are WARNED!* * - * ```js + * @example <caption>serializing regex, date, buffer, ...</caption> * var str = '{obj: {foo: "bar"}, arr: [1, "2"], regexp: /^test?$/, date: new Date("2016-04-15T16:22:52.009Z")}' * var res = deserialize(str) * console.log(res) * //> { obj: { foo: 'bar' }, * //> arr: [ 1, '2' ], * //> regexp: /^test?$/, * //> date: Sat Apr 16 2016 01:22:52 GMT+0900 (JST) } - * ``` * + * @throws {Error|TypeError} parsing error * @param {String} str - string containing serialized data + * @param {Boolean} [unsafe] - if `true` unsafe and harmful code evaluation (default=false) * @return {Any} deserialized data */ -function deserialize (str) { - return (new Function('return ' + str))() +function deserialize (str, unsafe) { + if (!unsafe) str = sanitize(str) + return (new Function('"use strict"; return ' + str))() } module.exports = deserialize
lib/internal/sanitize.js+39 −0 added@@ -0,0 +1,39 @@ +'use strict' + +var esprima = require('esprima') + +/** +* remove `new Function`, `(function` and `eval` from `str` +* @param {String} str +* @return {String} +*/ +module.exports = function sanitize (str) { + var tokens = esprima.tokenize(str) + // console.log(tokens) + var i = 0 + while (i < tokens.length - 1) { + var arr = tokens.slice(i, i + 2) + var x = arr[0] + var y = arr[1] + if ((x.type === 'Identifier' && x.value === 'eval')) { + tokens.splice(i, 1) + } else if ( + (x.type === 'Punctuator' && y.type === 'Keyword' && /Function/i.test(y.value)) || + (y.type === 'Identifier' && y.value === 'eval') + ) { + tokens.splice(i + 1, 1) + } else if ( + (x.type === 'Keyword' && x.value === 'new' && + y.type === 'Identifier' && /Function/i.test(y.value)) || + (x.type === 'Punctuator' && x.value === '(' && + y.type === 'Punctuator' && y.value === ')') + ) { + tokens.splice(i, 2) + } + i++ + } + return tokens.map(function (t) { + var sp = (t.type === 'Keyword') ? ' ' : '' + return t.value + sp + }).join('') +}
lib/serialize.js+2 −8 modified@@ -12,9 +12,7 @@ var Ref = require('./internal/reference') /** * serializes an object to javascript * - * #### Example - serializing regex, date, buffer, ... - * - * ```js + * @example <caption>serializing regex, date, buffer, ...</caption> * var serialize = require('serialize-to-js').serialize; * var obj = { * str: '<script>var a = 0 > 1</script>', @@ -30,11 +28,8 @@ var Ref = require('./internal/reference') * } * console.log(serialize(obj)) * // > {str: "\u003Cscript\u003Evar a = 0 \u003E 1\u003C\u002Fscript\u003E", num: 3.1415, bool: true, nil: null, undef: undefined, obj: {foo: "bar"}, arr: [1, "2"], regexp: /^test?$/, date: new Date("2016-04-15T16:22:52.009Z"), buffer: new Buffer('ZGF0YQ==', 'base64')} - * ``` - * - * #### Example - serializing while respecting references * - * ```js + * @example <caption>serializing while respecting references</caption> * var serialize = require('serialize-to-js').serialize; * var obj = { object: { regexp: /^test?$/ } }; * obj.reference = obj.object; @@ -43,7 +38,6 @@ var Ref = require('./internal/reference') * //> {object: {regexp: /^test?$/}} * console.log(opts.references); * //> [ [ '.reference', '.object' ] ] - * ``` * * @param {Object|Array|Function|Any} source - source to serialize * @param {Object} [opts] - options
lib/serializeToModule.js+1 −4 modified@@ -11,9 +11,7 @@ var serialize = require('./serialize') /** * serialize to a module which can be `require`ed. * - * #### Example - serializing while respecting references - * - * ```js + * @example <caption>serializing while respecting references</caption> * var serialTM = require('serialize-to-js').serializeToModule; * var obj = { object: { regexp: /^test?$/ } }; * obj.reference = obj.object; @@ -24,7 +22,6 @@ var serialize = require('./serialize') * //> } * //> } * //> m.reference = m.object - * ``` * * @param {Object|Array|Function|Any} source - source to serialize * @param {Object} [opts] - options
package.json+4 −3 modified@@ -11,20 +11,21 @@ "test": "test" }, "dependencies": { + "esprima": "^3.1.3", "js-beautify": "~1.6.8" }, "devDependencies": { "eslint": "^3.13.1", - "eslint-config-standard": "^6.2.1", + "eslint-config-standard": "^7.0.0-beta.0", "eslint-plugin-promise": "^3.4.0", "eslint-plugin-standard": "^2.0.1", "istanbul": "^0.4.5", "mocha": "^3.2.0", "rimraf": "^2.5.4" }, "scripts": { - "test": "mocha --reporter spec --check-leaks test/*.mocha.js", - "cover": "istanbul cover _mocha --report lcov --report text -- --reporter dot --check-leaks test/*.mocha.js", + "test": "mocha --reporter spec --check-leaks test/*.js", + "cover": "istanbul cover _mocha --report lcov --report text -- --reporter dot --check-leaks test/*.js", "doc": "jsdox -o doc lib/*.js", "lint": "eslint --quiet '**/*.js'", "readme": "markedpp --githubid -i README.md -o README.md",
README.md+6 −0 modified@@ -98,6 +98,10 @@ console.log(opts.references); deserialize a serialized object to javascript +> _NOTE_: Deserialization uses `new Function()` for code evaluation which may be "harmful". +> In default mode input code gets inspected, but removing `new Function, function, eval` might still not be sufficient. +> **SO NOW YOU ARE WARNED!** + #### Example - deserializing regex, date, ... ```js @@ -114,6 +118,8 @@ console.log(res) **str**: `String`, string containing serialized data +**unsafe**: `Boolean`, if `true` unsafe and harmful code evaluation (default=false) + **Returns**: `Any`, deserialized data
test/index.js+17 −0 renamed@@ -368,3 +368,20 @@ describe('#serialize and deserialize', function () { assert.deepEqual(res, obj) }) }) + +describe('#deserialize', function () { + it('harmful IIFE using `function` should get removed and deserialize should throw', function () { + var str = "(\n\tfunction(){ global.x = 'test'; eval('console.log(`exploited`)') })()" + assert.throws( + function () { + M.deserialize(str) + }, /SyntaxError/ + ) + }) + it('harmful IIFE using `new Function` should get removed', function () { + var str = "new Function('return console.log(`exploited`)')()" + var res = M.deserialize(str) + assert.equal(typeof res, 'string') + assert.equal(res, 'return console.log(`exploited`)') + }) +})
test/sanitize.js+30 −0 added@@ -0,0 +1,30 @@ +/* eslint no-new-func: 0 */ +/* global describe, it */ + +'use strict' + +var assert = require('assert') +var sanitize = require('../lib/internal/sanitize') + +describe('#sanitize', function () { + it('should remove "function"', function () { + var res = sanitize('(function(){console.log(`exploited`)}))()') + var exp = '({console.log(`exploited`)}))' + assert.equal(res, exp) + }) + it('should remove "new Function"', function () { + var res = sanitize('new Function(console.log(`exploited`))()') + var exp = '(console.log(`exploited`))' + assert.equal(res, exp) + }) + it('should remove "eval"', function () { + var res = sanitize('(\n\tfunction(){eval(\'console.log(`exploited`)\') })()') + var exp = "({('console.log(`exploited`)')})" + assert.equal(res, exp) + }) + it('should not join new Date', function () { + var res = sanitize('new Date("1970-01-01T"00:00:00)') + var exp = 'new Date("1970-01-01T"00:00:00)' + assert.equal(res, exp) + }) +})
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
9- github.com/commenthol/serialize-to-js/issues/1nvdIssue TrackingPatchThird Party AdvisoryWEB
- opsecx.com/index.php/2017/02/08/exploiting-node-js-deserialization-bug-for-remote-code-execution/nvdExploitThird Party Advisory
- www.securityfocus.com/bid/96223nvdThird Party AdvisoryVDB EntryWEB
- github.com/advisories/GHSA-mm62-wxc8-cf7mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2017-5954ghsaADVISORY
- github.com/commenthol/serialize-to-js/commit/1cd433960e5b9db4c0b537afb28366198a319429ghsaWEB
- opsecx.com/index.php/2017/02/08/exploiting-node-js-deserialization-bug-for-remote-code-executionghsaWEB
- www.npmjs.com/advisories/313ghsaWEB
- www.npmjs.com/package/serialize-to-jsghsaWEB
News mentions
0No linked articles in our index yet.