Skip to content

Commit

Permalink
safer-eval for deserialization
Browse files Browse the repository at this point in the history
  • Loading branch information
commenthol committed Mar 5, 2017
1 parent 01ac477 commit 0cea49e
Show file tree
Hide file tree
Showing 18 changed files with 396 additions and 380 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
coverage/*
tmp/*
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
node_modules/
node_modules
coverage/
doc/
tmp/
*.log
*.tgz
*.sh
5 changes: 3 additions & 2 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
node_modules/
node_modules
coverage/
test/
doc/
tmp/
Makefile
.*
*.log
*.tgz
Makefile
*.sh
22 changes: 22 additions & 0 deletions .zuul.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
ui: mocha-bdd
tunnel_host: http://focusaurus.com
browsers:
- name: chrome
version: latest
platform: Windows 10
- name: firefox
version:
- 45
- latest
- name: safari
version: latest
- name: microsoftedge
version: latest
# - name: iphone
# version: latest
# Not Supported
# - name: internet explorer
# version: 11
# platform: Windows 10
# - name: internet explorer
# version: 9..latest
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ readme: README.md
v%:
n $@ && npm test

.PHONY: all readme
zuul:
node_modules/.bin/zuul --local 3000 test/*.js

.PHONY: all readme zuul
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,15 @@ console.log(opts.references);

### deserialize

`deserialize(str)`
`deserialize(str, [context])`

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!**
Uses [safer-eval][] for deserialization.

#### Example - deserializing regex, date, ...

```js
Expand All @@ -118,7 +119,7 @@ console.log(res)

**str**: `String`, string containing serialized data

**unsafe**: `Boolean`, if `true` unsafe and harmful code evaluation (default=false)
**context**: (optional) pass context e.g. if requiring Buffer use `{Buffer: Buffer}`.

**Returns**: `Any`, deserialized data

Expand Down Expand Up @@ -176,3 +177,4 @@ Copyright (c) 2016- commenthol (MIT License)
See [LICENSE][] for more info.

[LICENSE]: ./LICENSE
[safer-eval]: https://github.com/commenthol/safer-eval
17 changes: 9 additions & 8 deletions lib/deserialize.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
/*
* @copyright 2016 commenthol
* @copyright 2016- commenthol
* @license MIT
*/
/* eslint no-new-func: 0 */

'use strict'

var sanitize = require('./internal/sanitize')
var saferEval = require('safer-eval')

/**
* 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.
* _NOTE_: Deserialization uses `safer-eval` for code evaluation which may be "harmful".
* *So now you are WARNED!*
*
* @example <caption>serializing regex, date, buffer, ...</caption>
Expand All @@ -26,11 +25,13 @@ var sanitize = require('./internal/sanitize')
*
* @throws {Error|TypeError} parsing error
* @param {String} str - string containing serialized data
* @param {Boolean} [unsafe] - if `true` unsafe and harmful code evaluation (default=false)
* @param {Object|Boolean} [context] - pass context - if `true` unsafe execution
* @return {Any} deserialized data
*/
function deserialize (str, unsafe) {
if (!unsafe) str = sanitize(str)
return (new Function('"use strict"; return ' + str))()
function deserialize (str, context) {
if (context === true) {
return (new Function('"use strict"; return ' + str))() // unsafe execution
}
return saferEval(str, context)
}
module.exports = deserialize
2 changes: 1 addition & 1 deletion lib/internal/reference.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* @copyright 2015 commenthol
* @copyright 2015- commenthol
* @license MIT
*/

Expand Down
39 changes: 0 additions & 39 deletions lib/internal/sanitize.js

This file was deleted.

2 changes: 1 addition & 1 deletion lib/serialize.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* @copyright 2016 commenthol
* @copyright 2016- commenthol
* @license MIT
*/

Expand Down
2 changes: 1 addition & 1 deletion lib/serializeToModule.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* @copyright 2016 commenthol
* @copyright 2016- commenthol
* @license MIT
*/

Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
{
"name": "serialize-to-js",
"version": "1.0.0",
"version": "1.1.0-0",
"description": "serialize objects to javascript",
"main": "lib",
"engines": {
"node": ">=0.8.0"
"node": ">=4.0.0"
},
"directories": {
"doc": "doc",
"test": "test"
},
"dependencies": {
"esprima": "^3.1.3",
"js-beautify": "~1.6.8"
"js-beautify": "~1.6.8",
"safer-eval": "^1.2.0"
},
"devDependencies": {
"eslint": "^3.13.1",
Expand All @@ -24,7 +24,7 @@
"rimraf": "^2.5.4"
},
"scripts": {
"test": "mocha --reporter spec --check-leaks test/*.js",
"test": "mocha 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'",
Expand Down
85 changes: 85 additions & 0 deletions test/deserialize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/* eslint no-new-func:0 */
/* global describe, it */

'use strict'

var assert = require('assert')
var M = require('..')
var serialize = M.serialize
var deserialize = M.deserialize
var fixtures = require('./fixtures')

function log (arg) {
console.log(JSON.stringify(arg))
}

describe('#deserialize', function () {
describe('simple', function () {
Object.keys(fixtures).forEach(function (tcName) {
it(tcName, function () {
var tc = fixtures[tcName]
var inp = tc[1]
var exp = tc[0]
var fn = tc[2]
var res = deserialize(inp)
switch (fn) {
case 'toString':
assert.strictEqual(res.toString(), exp.toString())
break
default:
assert.deepEqual(res, exp)
}
})
})
})

describe('safer operation', function () {
it('harmful IIFE using `function` should throw - no deserialize', function () {
var str = "(function(){ global.exploit = 'test'; eval('console.log(`exploited`)') })()"
assert.throws(function () {
deserialize(str)
})
})
it('should throw on using `new Function`', function () {
assert.throws(function () {
var str = "new Function('Array.prototype.join = 1')()"
deserialize(str)
})
})
it('should not overwrite built-in objects', function () {
deserialize("(Object.assign = 'exploited')")
assert.ok(Object.assign !== 'exploited')
})
})

describe('unsafe operation', function () {
it('should not throw on using `new Function`', function () {
var str = "new Function('return 25 + 9')()"
var res = deserialize(str, true)
assert.equal(res, 34)
})
it('should not throw on using `eval`', function () {
var str = "eval('25 + 9')"
var res = deserialize(str, true)
assert.equal(res, 34)
})
})

describe('serialize and deserialize', function () {
it('an object', function () {
var obj = {
str: '<script>var a = 0 > 1</script>',
num: 3.1415,
bool: true,
nil: null,
undef: undefined,
obj: {foo: 'bar'},
arr: [1, '2'],
regexp: /^test?$/,
date: new Date()
}
var res = deserialize(serialize(obj), {})
assert.deepEqual(res, obj)
})
})
})
Loading

0 comments on commit 0cea49e

Please sign in to comment.