qs 6.9.7 → 6.10.3

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;
@@ -55,6 +56,8 @@ var isNonNullishPrimitive = function isNonNullishPrimitive(v) {
55
56
  || typeof v === 'bigint';
56
57
  };
57
58
 
59
+ var sentinel = {};
60
+
58
61
  var stringify = function stringify(
59
62
  object,
60
63
  prefix,
@@ -69,9 +72,30 @@ var stringify = function stringify(
69
72
  format,
70
73
  formatter,
71
74
  encodeValuesOnly,
72
- charset
75
+ charset,
76
+ sideChannel
73
77
  ) {
74
78
  var obj = object;
79
+
80
+ var tmpSc = sideChannel;
81
+ var step = 0;
82
+ var findFlag = false;
83
+ while ((tmpSc = tmpSc.get(sentinel)) !== void undefined && !findFlag) {
84
+ // Where object last appeared in the ref tree
85
+ var pos = tmpSc.get(object);
86
+ step += 1;
87
+ if (typeof pos !== 'undefined') {
88
+ if (pos === step) {
89
+ throw new RangeError('Cyclic object value');
90
+ } else {
91
+ findFlag = true; // Break while
92
+ }
93
+ }
94
+ if (typeof tmpSc.get(sentinel) === 'undefined') {
95
+ step = 0;
96
+ }
97
+ }
98
+
75
99
  if (typeof filter === 'function') {
76
100
  obj = filter(prefix, obj);
77
101
  } else if (obj instanceof Date) {
@@ -138,6 +162,9 @@ var stringify = function stringify(
138
162
  ? typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix
139
163
  : prefix + (allowDots ? '.' + key : '[' + key + ']');
140
164
 
165
+ sideChannel.set(object, step);
166
+ var valueSideChannel = getSideChannel();
167
+ valueSideChannel.set(sentinel, sideChannel);
141
168
  pushToArray(values, stringify(
142
169
  value,
143
170
  keyPrefix,
@@ -152,7 +179,8 @@ var stringify = function stringify(
152
179
  format,
153
180
  formatter,
154
181
  encodeValuesOnly,
155
- charset
182
+ charset,
183
+ valueSideChannel
156
184
  ));
157
185
  }
158
186
 
@@ -246,6 +274,7 @@ module.exports = function (object, opts) {
246
274
  objKeys.sort(options.sort);
247
275
  }
248
276
 
277
+ var sideChannel = getSideChannel();
249
278
  for (var i = 0; i < objKeys.length; ++i) {
250
279
  var key = objKeys[i];
251
280
 
@@ -266,7 +295,8 @@ module.exports = function (object, opts) {
266
295
  options.format,
267
296
  options.formatter,
268
297
  options.encodeValuesOnly,
269
- options.charset
298
+ options.charset,
299
+ sideChannel
270
300
  ));
271
301
  }
272
302
 
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.7",
5
+ "version": "6.10.3",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/ljharb/qs.git"
@@ -29,6 +29,9 @@
29
29
  "engines": {
30
30
  "node": ">=0.6"
31
31
  },
32
+ "dependencies": {
33
+ "side-channel": "^1.0.4"
34
+ },
32
35
  "devDependencies": {
33
36
  "@ljharb/eslint-config": "^20.1.0",
34
37
  "aud": "^1.1.5",
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
@@ -439,7 +439,7 @@ test('stringify()', function (t) {
439
439
  st.end();
440
440
  });
441
441
 
442
- t.test('doesn\'t blow up when Buffer global is missing', function (st) {
442
+ t.test('does not blow up when Buffer global is missing', function (st) {
443
443
  var tempBuffer = global.Buffer;
444
444
  delete global.Buffer;
445
445
  var result = qs.stringify({ a: 'b', c: 'd' });
@@ -448,6 +448,57 @@ test('stringify()', function (t) {
448
448
  st.end();
449
449
  });
450
450
 
451
+ t.test('does not crash when parsing circular references', function (st) {
452
+ var a = {};
453
+ a.b = a;
454
+
455
+ st['throws'](
456
+ function () { qs.stringify({ 'foo[bar]': 'baz', 'foo[baz]': a }); },
457
+ /RangeError: Cyclic object value/,
458
+ 'cyclic values throw'
459
+ );
460
+
461
+ var circular = {
462
+ a: 'value'
463
+ };
464
+ circular.a = circular;
465
+ st['throws'](
466
+ function () { qs.stringify(circular); },
467
+ /RangeError: Cyclic object value/,
468
+ 'cyclic values throw'
469
+ );
470
+
471
+ var arr = ['a'];
472
+ st.doesNotThrow(
473
+ function () { qs.stringify({ x: arr, y: arr }); },
474
+ 'non-cyclic values do not throw'
475
+ );
476
+
477
+ st.end();
478
+ });
479
+
480
+ t.test('non-circular duplicated references can still work', function (st) {
481
+ var hourOfDay = {
482
+ 'function': 'hour_of_day'
483
+ };
484
+
485
+ var p1 = {
486
+ 'function': 'gte',
487
+ arguments: [hourOfDay, 0]
488
+ };
489
+ var p2 = {
490
+ 'function': 'lte',
491
+ arguments: [hourOfDay, 23]
492
+ };
493
+
494
+ st.equal(
495
+ qs.stringify({ filters: { $and: [p1, p2] } }, { encodeValuesOnly: true }),
496
+ 'filters[$and][0][function]=gte&filters[$and][0][arguments][0][function]=hour_of_day&filters[$and][0][arguments][1]=0&filters[$and][1][function]=lte&filters[$and][1][arguments][0][function]=hour_of_day&filters[$and][1][arguments][1]=23'
497
+ );
498
+
499
+ st.end();
500
+ });
501
+
451
502
  t.test('selects properties when filter=array', function (st) {
452
503
  st.equal(qs.stringify({ a: 'b' }, { filter: ['a'] }), 'a=b');
453
504
  st.equal(qs.stringify({ a: 1 }, { filter: [] }), '');
@@ -800,5 +851,15 @@ test('stringify()', function (t) {
800
851
  st.end();
801
852
  });
802
853
 
854
+ t.test('stringifies sparse arrays', function (st) {
855
+ /* eslint no-sparse-arrays: 0 */
856
+ st.equal(qs.stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true }), 'a[1]=2&a[4]=1');
857
+ st.equal(qs.stringify({ a: [, { b: [, , { c: '1' }] }] }, { encodeValuesOnly: true }), 'a[1][b][2][c]=1');
858
+ st.equal(qs.stringify({ a: [, [, , [, , , { c: '1' }]]] }, { encodeValuesOnly: true }), 'a[1][2][3][c]=1');
859
+ st.equal(qs.stringify({ a: [, [, , [, , , { c: [, '1'] }]]] }, { encodeValuesOnly: true }), 'a[1][2][3][c][1]=1');
860
+
861
+ st.end();
862
+ });
863
+
803
864
  t.end();
804
865
  });