diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 index 0f350c24..d4569487 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,4 @@ -Copyright (c) 2014 Nathan LaFreniere and other contributors - +Copyright (c) 2014 Nathan LaFreniere and other contributors. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -9,9 +8,9 @@ modification, are permitted provided that the following conditions are met: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Walmart nor the names of any contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. + * The names of any contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -26,4 +25,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * -The complete list of contributors can be found at: https://github.com/hapijs/riddler/graphs/contributors +The complete list of contributors can be found at: https://github.com/hapijs/qs/graphs/contributors diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 99008ffd..f86ffc9b --- a/README.md +++ b/README.md @@ -1,29 +1,26 @@ - +# qs -Riddler is a querystring parsing and stringifying library with some added security +A querystring parsing and stringifying library with some added security. -[![Build Status](https://secure.travis-ci.org/hapijs/riddler.svg)](http://travis-ci.org/hapijs/riddler) +[![Build Status](https://secure.travis-ci.org/hapijs/qs.svg)](http://travis-ci.org/hapijs/qs) Lead Maintainer: [Nathan LaFreniere](https://github.com/nlf) +The **qs** module was original created and maintained by [TJ Holowaychuk](https://github.com/visionmedia/node-querystring). ## Usage ```javascript -var Riddler = require('riddler'); +var Qs = require('qs'); -var obj = Riddler.parse('a=c'); -console.log(obj); // { a: 'c' } - -var str = Riddler.stringify(obj); -console.log(str); // 'a=c' +var obj = Qs.parse('a=c'); // { a: 'c' } +var str = Qs.stringify(obj); // 'a=c' ``` ### Objects -Riddler allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]`. - -For example, in order to create an object: +**qs** allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]`. +For example, the string `'foo[bar]=baz'` converts to: ```javascript { @@ -33,16 +30,20 @@ For example, in order to create an object: } ``` -One would use the string `foo[bar]=baz`. - -You can also nest your objects: +You can also nest your objects, like `'foo[bar][baz]=foobarbaz'`: ```javascript -var obj = Riddler.parse('foo[bar][baz]=foobarbaz'); -// obj = { foo: { bar: { baz: 'foobarbaz' } } } +{ + foo: { + bar: { + baz: 'foobarbaz' + } + } +} ``` -By default, when nesting objects riddler will only parse up to 5 children deep. This means if you attempt to parse a string like `a[b][c][d][e][f][g][h][i]=j` your resulting object will be: +By default, when nesting objects **qs** will only parse up to 5 children deep. This means if you attempt to parse a string like +`'a[b][c][d][e][f][g][h][i]=j'` your resulting object will be: ```javascript { @@ -62,57 +63,58 @@ By default, when nesting objects riddler will only parse up to 5 children deep. } ``` -This depth can be overridden by passing a `depth` parameter to `riddler.parse()`: +This depth can be overridden by passing a `depth` option to `Qs.parse(string, depth)`: ```javascript -Riddler.parse('a[b][c][d][e][f][g][h][i]=j', 1); +Qs.parse('a[b][c][d][e][f][g][h][i]=j', 1); // { a: { b: { '[c][d][e][f][g][h][i]': 'j' } } } ``` -Having this limit helps mitigate abuse when riddler is used to parse user input, and it is recommended to keep it a reasonably small number. +The depth limit mitigate abuse when **qs** is used to parse user input, and it is recommended to keep it a reasonably small number. ### Arrays -Riddler can also parse arrays using a similar `[]` notation: +**qs** can also parse arrays using a similar `[]` notation: ```javascript -Riddler.parse('a[]=b&a[]=c'); +Qs.parse('a[]=b&a[]=c'); // { a: ['b', 'c'] } ``` You may specify an index as well: ```javascript -Riddler.parse('a[1]=c&a[0]=b'); +Qs.parse('a[1]=c&a[0]=b'); // { a: ['b', 'c'] } ``` -Note that the only difference between an index in an array and a key in an object is that the value between the brackets must be a number to create an array. - -When creating arrays with specific indices, riddler will compact a sparse array to only the existing values preserving their order: +Note that the only difference between an index in an array and a key in an object is that the value between the brackets must be a number +to create an array. When creating arrays with specific indices, **qs** will compact a sparse array to only the existing values preserving +their order: ```javascript -Riddler.parse('a[1]=b&a[15]=c'); +Qs.parse('a[1]=b&a[15]=c'); // { a: ['b', 'c'] } ``` -Riddler will also limit specifying indices in an array to a maximum index of `20`. Any array members with an index of greater than `20` will instead be converted to an object with the index as the key: +**qs** will also limit specifying indices in an array to a maximum index of `20`. Any array members with an index of greater than `20` will +instead be converted to an object with the index as the key: ```javascript -Riddler.parse('a[100]=b'); +Qs.parse('a[100]=b'); // { a: { '100': 'b' } } ``` -If you mix notations, riddler will merge the two items into an object: +If you mix notations, **qs** will merge the two items into an object: ```javascript -Riddler.parse('a[0]=b&a[b]=c'); +Qs.parse('a[0]=b&a[b]=c'); // { a: { '0': 'b', b: 'c' } } ``` You can also create arrays of objects: ```javascript -Riddler.parse('a[][b]=c'); +Qs.parse('a[][b]=c'); // { a: [{ b: 'c' }] } -``` +``` \ No newline at end of file diff --git a/coverage.html b/coverage.html new file mode 100644 index 00000000..321c2113 --- /dev/null +++ b/coverage.html @@ -0,0 +1,3158 @@ + + +
+Line | +Hits | +Source | +
---|---|---|
1 | +1 | +module.exports = require('./lib'); | +
2 | ++ | + |
Line | +Hits | +Source | +
---|---|---|
1 | ++ | // Load modules | +
2 | ++ | + |
3 | +1 | +var Stringify = require('./stringify'); | +
4 | +1 | +var Parse = require('./parse'); | +
5 | ++ | + |
6 | ++ | + |
7 | ++ | // Declare internals | +
8 | ++ | + |
9 | +1 | +var internals = {}; | +
10 | ++ | + |
11 | ++ | + |
12 | +1 | +module.exports = { | +
13 | ++ | stringify: Stringify, | +
14 | ++ | parse: Parse | +
15 | ++ | }; | +
16 | ++ | + |
Line | +Hits | +Source | +
---|---|---|
1 | ++ | // Load modules | +
2 | ++ | + |
3 | +1 | +var Utils = require('./utils'); | +
4 | ++ | + |
5 | ++ | + |
6 | ++ | // Declare internals | +
7 | ++ | + |
8 | +1 | +var internals = { | +
9 | ++ | depth: 5, | +
10 | ++ | arrayLimit: 20, | +
11 | ++ | parametersLimit: 1000 | +
12 | ++ | }; | +
13 | ++ | + |
14 | ++ | + |
15 | +1 | +internals.parseValues = function (str) { | +
16 | ++ | + |
17 | +51 | +var obj = {}; | +
18 | +51 | +var parts = str.split('&').slice(0, internals.parametersLimit); | +
19 | ++ | + |
20 | +51 | +for (var i = 0, il = parts.length; i < il; ++i) { | +
21 | +1074 | +var part = parts[i]; | +
22 | +1074 | +var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1; | +
23 | ++ | + |
24 | +1074 | +if (pos === -1) { | +
25 | +6 | +obj[Utils.decode(part)] = ''; | +
26 | ++ | } | +
27 | ++ | else { | +
28 | +1068 | +var key = Utils.decode(part.slice(0, pos)); | +
29 | +1068 | +var val = Utils.decode(part.slice(pos + 1)); | +
30 | ++ | + |
31 | +1068 | +if (!obj[key]) { | +
32 | +61 | +obj[key] = val; | +
33 | ++ | } | +
34 | ++ | else { | +
35 | +1007 | +obj[key] = [].concat(obj[key]).concat(val); | +
36 | ++ | } | +
37 | ++ | } | +
38 | ++ | } | +
39 | ++ | + |
40 | +51 | +return obj; | +
41 | ++ | }; | +
42 | ++ | + |
43 | ++ | + |
44 | +1 | +internals.parseObject = function (chain, val) { | +
45 | ++ | + |
46 | +192 | +if (!chain.length) { | +
47 | +69 | +return val; | +
48 | ++ | } | +
49 | ++ | + |
50 | +123 | +var root = chain.shift(); | +
51 | ++ | + |
52 | +123 | +var obj = {}; | +
53 | +123 | +if (root === '[]') { | +
54 | +10 | +obj = []; | +
55 | +10 | +obj = obj.concat(internals.parseObject(chain, val)); | +
56 | ++ | } | +
57 | ++ | else { | +
58 | +113 | +var cleanRoot = root[0] === '[' && root[root.length - 1] === ']' ? root.slice(1, root.length - 1) : root; | +
59 | +113 | +var index = parseInt(cleanRoot, 10); | +
60 | +113 | +if (!isNaN(index) && | +
61 | ++ | root !== cleanRoot && | +
62 | ++ | index <= internals.arrayLimit) { | +
63 | ++ | + |
64 | +12 | +obj = []; | +
65 | +12 | +obj[index] = internals.parseObject(chain, val); | +
66 | ++ | } | +
67 | ++ | else { | +
68 | +101 | +obj[cleanRoot] = internals.parseObject(chain, val); | +
69 | ++ | } | +
70 | ++ | } | +
71 | ++ | + |
72 | +123 | +return obj; | +
73 | ++ | }; | +
74 | ++ | + |
75 | ++ | + |
76 | +1 | +internals.parseKeys = function (key, val, depth) { | +
77 | ++ | + |
78 | +73 | +if (!key) { | +
79 | +1 | +return; | +
80 | ++ | } | +
81 | ++ | + |
82 | +72 | +depth = typeof depth === 'undefined' ? internals.depth : depth; | +
83 | ++ | + |
84 | ++ | // The regex chunks | +
85 | ++ | + |
86 | +72 | +var parent = /^([^\[\]]*)/; | +
87 | +72 | +var child = /(\[[^\[\]]*\])/g; | +
88 | ++ | + |
89 | ++ | // Get the parent | +
90 | ++ | + |
91 | +72 | +var segment = parent.exec(key); | +
92 | ++ | + |
93 | ++ | // Don't allow them to overwrite object prototype properties | +
94 | ++ | + |
95 | +72 | +if (Object.prototype.hasOwnProperty(segment[1])) { | +
96 | +3 | +return; | +
97 | ++ | } | +
98 | ++ | + |
99 | ++ | // Stash the parent if it exists | +
100 | ++ | + |
101 | +69 | +var keys = []; | +
102 | +69 | +if (segment[1]) { | +
103 | +67 | +keys.push(segment[1]); | +
104 | ++ | } | +
105 | ++ | + |
106 | ++ | // Loop through children appending to the array until we hit depth | +
107 | ++ | + |
108 | +69 | +var i = 0; | +
109 | +69 | +while ((segment = child.exec(key)) !== null && i < depth) { | +
110 | ++ | + |
111 | +55 | +++i; | +
112 | +55 | +if (!Object.prototype.hasOwnProperty(segment[1].replace(/\[|\]/g, ''))) { | +
113 | +53 | +keys.push(segment[1]); | +
114 | ++ | } | +
115 | ++ | } | +
116 | ++ | + |
117 | ++ | // If there's a remainder, just add whatever is left | +
118 | ++ | + |
119 | +69 | +if (segment) { | +
120 | +3 | +keys.push('[' + key.slice(segment.index) + ']'); | +
121 | ++ | } | +
122 | ++ | + |
123 | +69 | +return internals.parseObject(keys, val); | +
124 | ++ | }; | +
125 | ++ | + |
126 | ++ | + |
127 | +1 | +module.exports = function (str, depth) { | +
128 | ++ | + |
129 | +57 | +if (str === '' || | +
130 | ++ | str === null || | +
131 | ++ | typeof str === 'undefined') { | +
132 | ++ | + |
133 | +3 | +return {}; | +
134 | ++ | } | +
135 | ++ | + |
136 | +54 | +var tempObj = typeof str === 'string' ? internals.parseValues(str) : Utils.clone(str); | +
137 | +54 | +var obj = {}; | +
138 | ++ | + |
139 | ++ | // Iterate over the keys and setup the new object | +
140 | ++ | + |
141 | +54 | +for (var key in tempObj) { | +
142 | +74 | +if (tempObj.hasOwnProperty(key)) { | +
143 | +73 | +var newObj = internals.parseKeys(key, tempObj[key], depth); | +
144 | +73 | +obj = Utils.merge(obj, newObj); | +
145 | ++ | } | +
146 | ++ | } | +
147 | ++ | + |
148 | +54 | +return Utils.compact(obj); | +
149 | ++ | }; | +
150 | ++ | + |
151 | ++ | + |
152 | ++ | + |
Line | +Hits | +Source | +
---|---|---|
1 | ++ | // Load modules | +
2 | ++ | + |
3 | ++ | + |
4 | ++ | // Declare internals | +
5 | ++ | + |
6 | +1 | +var internals = {}; | +
7 | ++ | + |
8 | ++ | + |
9 | +1 | +internals.stringify = function (obj, prefix) { | +
10 | ++ | + |
11 | +40 | +if (typeof obj === 'string' || | +
12 | ++ | typeof obj === 'number') { | +
13 | ++ | + |
14 | +21 | +return [prefix + '=' + encodeURIComponent(obj)]; | +
15 | ++ | } | +
16 | ++ | + |
17 | +19 | +if (obj === null) { | +
18 | +3 | +return [prefix]; | +
19 | ++ | } | +
20 | ++ | + |
21 | +16 | +var values = []; | +
22 | ++ | + |
23 | +16 | +for (var key in obj) { | +
24 | ++ | if ( obj.hasOwnProperty(key) ) { |
+
25 | +20 | +values = values.concat(internals.stringify(obj[key], prefix + '[' + encodeURIComponent(key) + ']')); | +
26 | ++ | } | +
27 | ++ | } | +
28 | ++ | + |
29 | +16 | +return values; | +
30 | ++ | }; | +
31 | ++ | + |
32 | ++ | + |
33 | +1 | +module.exports = function (obj) { | +
34 | ++ | + |
35 | +19 | +var keys = []; | +
36 | +19 | +var value = JSON.parse(JSON.stringify(obj)); | +
37 | ++ | + |
38 | +19 | +for (var key in value) { | +
39 | ++ | if ( value.hasOwnProperty(key) ) { |
+
40 | +20 | +keys = keys.concat(internals.stringify(value[key], encodeURIComponent(key))); | +
41 | ++ | } | +
42 | ++ | } | +
43 | ++ | + |
44 | +19 | +return keys.join('&'); | +
45 | ++ | }; | +
46 | ++ | + |
Line | +Hits | +Source | +
---|---|---|
1 | ++ | // Load modules | +
2 | ++ | + |
3 | ++ | + |
4 | ++ | // Declare internals | +
5 | ++ | + |
6 | +1 | +var internals = {}; | +
7 | ++ | + |
8 | ++ | + |
9 | +1 | +exports.arrayToObject = function (source) { | +
10 | ++ | + |
11 | +2 | +var obj = {}; | +
12 | +2 | +for (var i = 0, il = source.length; i < il; ++i) { | +
13 | +2 | +obj[i] = source[i]; | +
14 | ++ | } | +
15 | ++ | + |
16 | +2 | +return obj; | +
17 | ++ | }; | +
18 | ++ | + |
19 | ++ | + |
20 | +1 | +exports.clone = function (source) { | +
21 | ++ | + |
22 | +1209 | +if (typeof source !== 'object' || | +
23 | ++ | source === null) { | +
24 | ++ | + |
25 | +1071 | +return source; | +
26 | ++ | } | +
27 | ++ | + |
28 | +138 | +if (Buffer.isBuffer(source)) { | +
29 | +1 | +return source.toString(); | +
30 | ++ | } | +
31 | ++ | + |
32 | +137 | +var obj = Array.isArray(source) ? [] : {}; | +
33 | +137 | +for (var i in source) { | +
34 | +1095 | +if (source.hasOwnProperty(i)) { | +
35 | +1094 | +obj[i] = exports.clone(source[i]); | +
36 | ++ | } | +
37 | ++ | } | +
38 | ++ | + |
39 | +137 | +return obj; | +
40 | ++ | }; | +
41 | ++ | + |
42 | ++ | + |
43 | +1 | +exports.merge = function (target, source) { | +
44 | ++ | + |
45 | +83 | +if (!source) { | +
46 | +4 | +return target; | +
47 | ++ | } | +
48 | ++ | + |
49 | +79 | +var obj = exports.clone(target); | +
50 | ++ | + |
51 | +79 | +if (Array.isArray(source)) { | +
52 | +8 | +for (var i = 0, il = source.length; i < il; ++i) { | +
53 | +13 | +if (source[i] !== undefined) { | +
54 | +9 | +obj[i] = source[i]; | +
55 | ++ | } | +
56 | ++ | } | +
57 | ++ | + |
58 | +8 | +return obj; | +
59 | ++ | } | +
60 | ++ | + |
61 | +71 | +if (Array.isArray(obj)) { | +
62 | +2 | +obj = exports.arrayToObject(obj); | +
63 | ++ | } | +
64 | ++ | + |
65 | +71 | +var keys = Object.keys(source); | +
66 | +71 | +for (var k = 0, kl = keys.length; k < kl; ++k) { | +
67 | +71 | +var key = keys[k]; | +
68 | +71 | +var value = source[key]; | +
69 | ++ | + |
70 | +71 | +if (value && | +
71 | ++ | typeof value === 'object') { | +
72 | ++ | + |
73 | +43 | +if (!obj[key]) { | +
74 | +33 | +obj[key] = exports.clone(value); | +
75 | ++ | } | +
76 | ++ | else { | +
77 | +10 | +obj[key] = exports.merge(obj[key], value); | +
78 | ++ | } | +
79 | ++ | } | +
80 | ++ | else { | +
81 | +28 | +obj[key] = value; | +
82 | ++ | } | +
83 | ++ | } | +
84 | ++ | + |
85 | +71 | +return obj; | +
86 | ++ | }; | +
87 | ++ | + |
88 | ++ | + |
89 | +1 | +exports.decode = function (str) { | +
90 | ++ | + |
91 | +2142 | +try { | +
92 | +2142 | +return decodeURIComponent(str.replace(/\+/g, ' ')); | +
93 | ++ | } catch (e) { | +
94 | +2 | +return str; | +
95 | ++ | } | +
96 | ++ | }; | +
97 | ++ | + |
98 | ++ | + |
99 | +1 | +exports.compact = function (obj) { | +
100 | ++ | + |
101 | +137 | +if (typeof obj !== 'object') { | +
102 | +53 | +return obj; | +
103 | ++ | } | +
104 | ++ | + |
105 | +84 | +var compacted = {}; | +
106 | ++ | + |
107 | +84 | +for (var key in obj) { | +
108 | +97 | +if (obj.hasOwnProperty(key)) { | +
109 | +96 | +if (Array.isArray(obj[key])) { | +
110 | +13 | +compacted[key] = []; | +
111 | ++ | + |
112 | +13 | +for (var i = 0, l = obj[key].length; i < l; i++) { | +
113 | +1051 | +if (obj[key].hasOwnProperty(i) && | +
114 | ++ | obj[key][i]) { | +
115 | ++ | + |
116 | +1021 | +compacted[key].push(obj[key][i]); | +
117 | ++ | } | +
118 | ++ | } | +
119 | ++ | } | +
120 | ++ | else { | +
121 | +83 | +compacted[key] = exports.compact(obj[key]); | +
122 | ++ | } | +
123 | ++ | } | +
124 | ++ | } | +
125 | ++ | + |
126 | +84 | +return compacted; | +
127 | ++ | }; | +
128 | ++ | + |