qs 6.9.6 → 6.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/parse.js CHANGED
@@ -8,6 +8,7 @@ var isArray = Array.isArray;
8
8
  var defaults = {
9
9
  allowDots: false,
10
10
  allowPrototypes: false,
11
+ allowSparse: false,
11
12
  arrayLimit: 20,
12
13
  charset: 'utf-8',
13
14
  charsetSentinel: false,
@@ -217,6 +218,7 @@ var normalizeParseOptions = function normalizeParseOptions(opts) {
217
218
  return {
218
219
  allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots,
219
220
  allowPrototypes: typeof opts.allowPrototypes === 'boolean' ? opts.allowPrototypes : defaults.allowPrototypes,
221
+ allowSparse: typeof opts.allowSparse === 'boolean' ? opts.allowSparse : defaults.allowSparse,
220
222
  arrayLimit: typeof opts.arrayLimit === 'number' ? opts.arrayLimit : defaults.arrayLimit,
221
223
  charset: charset,
222
224
  charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
@@ -253,5 +255,9 @@ module.exports = function (str, opts) {
253
255
  obj = utils.merge(obj, newObj, options);
254
256
  }
255
257
 
258
+ if (options.allowSparse === true) {
259
+ return obj;
260
+ }
261
+
256
262
  return utils.compact(obj);
257
263
  };
package/lib/stringify.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var getSideChannel = require('side-channel');
3
4
  var utils = require('./utils');
4
5
  var formats = require('./formats');
5
6
  var has = Object.prototype.hasOwnProperty;
@@ -68,9 +69,15 @@ var stringify = function stringify(
68
69
  format,
69
70
  formatter,
70
71
  encodeValuesOnly,
71
- charset
72
+ charset,
73
+ sideChannel
72
74
  ) {
73
75
  var obj = object;
76
+
77
+ if (sideChannel.has(object)) {
78
+ throw new RangeError('Cyclic object value');
79
+ }
80
+
74
81
  if (typeof filter === 'function') {
75
82
  obj = filter(prefix, obj);
76
83
  } else if (obj instanceof Date) {
@@ -129,6 +136,7 @@ var stringify = function stringify(
129
136
  ? typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix
130
137
  : prefix + (allowDots ? '.' + key : '[' + key + ']');
131
138
 
139
+ sideChannel.set(object, true);
132
140
  pushToArray(values, stringify(
133
141
  value,
134
142
  keyPrefix,
@@ -143,7 +151,8 @@ var stringify = function stringify(
143
151
  format,
144
152
  formatter,
145
153
  encodeValuesOnly,
146
- charset
154
+ charset,
155
+ sideChannel
147
156
  ));
148
157
  }
149
158
 
@@ -237,6 +246,7 @@ module.exports = function (object, opts) {
237
246
  objKeys.sort(options.sort);
238
247
  }
239
248
 
249
+ var sideChannel = getSideChannel();
240
250
  for (var i = 0; i < objKeys.length; ++i) {
241
251
  var key = objKeys[i];
242
252
 
@@ -257,7 +267,8 @@ module.exports = function (object, opts) {
257
267
  options.format,
258
268
  options.formatter,
259
269
  options.encodeValuesOnly,
260
- options.charset
270
+ options.charset,
271
+ sideChannel
261
272
  ));
262
273
  }
263
274
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "qs",
3
3
  "description": "A querystring parser that supports nesting and arrays, with a depth limit",
4
4
  "homepage": "https://github.com/ljharb/qs",
5
- "version": "6.9.6",
5
+ "version": "6.10.0",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/ljharb/qs.git"
@@ -29,33 +29,36 @@
29
29
  "engines": {
30
30
  "node": ">=0.6"
31
31
  },
32
- "dependencies": {},
32
+ "dependencies": {
33
+ "side-channel": "^1.0.4"
34
+ },
33
35
  "devDependencies": {
34
- "@ljharb/eslint-config": "^17.3.0",
35
- "aud": "^1.1.3",
36
+ "@ljharb/eslint-config": "^17.5.1",
37
+ "aud": "^1.1.4",
36
38
  "browserify": "^16.5.2",
37
39
  "eclint": "^2.8.1",
38
- "eslint": "^7.17.0",
40
+ "eslint": "^7.22.0",
39
41
  "evalmd": "^0.0.19",
40
42
  "for-each": "^0.3.3",
41
- "has-symbols": "^1.0.1",
43
+ "has-symbols": "^1.0.2",
42
44
  "iconv-lite": "^0.5.1",
45
+ "in-publish": "^2.0.1",
43
46
  "mkdirp": "^0.5.5",
44
47
  "nyc": "^10.3.2",
45
48
  "object-inspect": "^1.9.0",
46
49
  "qs-iconv": "^1.0.4",
47
50
  "safe-publish-latest": "^1.1.4",
48
51
  "safer-buffer": "^2.1.2",
49
- "tape": "^5.1.1"
52
+ "tape": "^5.2.2"
50
53
  },
51
54
  "scripts": {
52
- "prepublish": "safe-publish-latest && npm run dist",
55
+ "prepublish": "safe-publish-latest && (not-in-publish || npm run dist)",
53
56
  "pretest": "npm run --silent readme && npm run --silent lint",
54
57
  "test": "npm run tests-only",
55
58
  "tests-only": "nyc tape 'test/**/*.js'",
56
59
  "posttest": "aud --production",
57
60
  "readme": "evalmd README.md",
58
- "postlint": "eclint check * lib/* test/*",
61
+ "postlint": "eclint check * lib/* test/* !dist/*",
59
62
  "lint": "eslint lib/*.js test/*.js",
60
63
  "dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js"
61
64
  },
package/test/parse.js CHANGED
@@ -269,6 +269,15 @@ test('parse()', function (t) {
269
269
  st.end();
270
270
  });
271
271
 
272
+ t.test('parses sparse arrays', function (st) {
273
+ /* eslint no-sparse-arrays: 0 */
274
+ st.deepEqual(qs.parse('a[4]=1&a[1]=2', { allowSparse: true }), { a: [, '2', , , '1'] });
275
+ st.deepEqual(qs.parse('a[1][b][2][c]=1', { allowSparse: true }), { a: [, { b: [, , { c: '1' }] }] });
276
+ st.deepEqual(qs.parse('a[1][2][3][c]=1', { allowSparse: true }), { a: [, [, , [, , , { c: '1' }]]] });
277
+ st.deepEqual(qs.parse('a[1][2][3][c][1]=1', { allowSparse: true }), { a: [, [, , [, , , { c: [, '1'] }]]] });
278
+ st.end();
279
+ });
280
+
272
281
  t.test('parses semi-parsed strings', function (st) {
273
282
  st.deepEqual(qs.parse({ 'a[b]': 'c' }), { a: { b: 'c' } });
274
283
  st.deepEqual(qs.parse({ 'a[b]': 'c', 'a[d]': 'e' }), { a: { b: 'c', d: 'e' } });
package/test/stringify.js CHANGED
@@ -433,7 +433,7 @@ test('stringify()', function (t) {
433
433
  st.end();
434
434
  });
435
435
 
436
- t.test('doesn\'t blow up when Buffer global is missing', function (st) {
436
+ t.test('does not blow up when Buffer global is missing', function (st) {
437
437
  var tempBuffer = global.Buffer;
438
438
  delete global.Buffer;
439
439
  var result = qs.stringify({ a: 'b', c: 'd' });
@@ -442,6 +442,29 @@ test('stringify()', function (t) {
442
442
  st.end();
443
443
  });
444
444
 
445
+ t.test('does not crash when parsing circular references', function (st) {
446
+ var a = {};
447
+ a.b = a;
448
+
449
+ st['throws'](
450
+ function () { qs.stringify({ 'foo[bar]': 'baz', 'foo[baz]': a }); },
451
+ RangeError,
452
+ 'cyclic values throw'
453
+ );
454
+
455
+ var circular = {
456
+ a: 'value'
457
+ };
458
+ circular.a = circular;
459
+ st['throws'](
460
+ function () { qs.stringify(circular); },
461
+ RangeError,
462
+ 'cyclic values throw'
463
+ );
464
+
465
+ st.end();
466
+ });
467
+
445
468
  t.test('selects properties when filter=array', function (st) {
446
469
  st.equal(qs.stringify({ a: 'b' }, { filter: ['a'] }), 'a=b');
447
470
  st.equal(qs.stringify({ a: 1 }, { filter: [] }), '');
@@ -789,5 +812,15 @@ test('stringify()', function (t) {
789
812
  st.end();
790
813
  });
791
814
 
815
+ t.test('stringifies sparse arrays', function (st) {
816
+ /* eslint no-sparse-arrays: 0 */
817
+ st.equal(qs.stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true }), 'a[1]=2&a[4]=1');
818
+ st.equal(qs.stringify({ a: [, { b: [, , { c: '1' }] }] }, { encodeValuesOnly: true }), 'a[1][b][2][c]=1');
819
+ st.equal(qs.stringify({ a: [, [, , [, , , { c: '1' }]]] }, { encodeValuesOnly: true }), 'a[1][2][3][c]=1');
820
+ st.equal(qs.stringify({ a: [, [, , [, , , { c: [, '1'] }]]] }, { encodeValuesOnly: true }), 'a[1][2][3][c][1]=1');
821
+
822
+ st.end();
823
+ });
824
+
792
825
  t.end();
793
826
  });