VYPR
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.

PackageAffected versionsPatched versions
serialize-to-jsnpm
< 1.0.01.0.0

Affected products

1

Patches

1
1cd433960e5b

harmful 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

News mentions

0

No linked articles in our index yet.