qs 6.11.1 → 6.11.2

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/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## **6.11.2**
2
+ - [Fix] `parse`: Fix parsing when the global Object prototype is frozen (#473)
3
+ - [Tests] add passing test cases with empty keys (#473)
4
+
1
5
  ## **6.11.1**
2
6
  - [Fix] `stringify`: encode comma values more consistently (#463)
3
7
  - [readme] add usage of `filter` option for injecting custom serialization, i.e. of custom types (#447)
package/dist/qs.js CHANGED
@@ -88,7 +88,8 @@ var isoSentinel = 'utf8=%26%2310003%3B'; // encodeURIComponent('✓')
88
88
  var charsetSentinel = 'utf8=%E2%9C%93'; // encodeURIComponent('✓')
89
89
 
90
90
  var parseValues = function parseQueryStringValues(str, options) {
91
- var obj = {};
91
+ var obj = { __proto__: null };
92
+
92
93
  var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str;
93
94
  var limit = options.parameterLimit === Infinity ? undefined : options.parameterLimit;
94
95
  var parts = cleanStr.split(options.delimiter, limit);
@@ -623,7 +624,7 @@ module.exports = function (object, opts) {
623
624
  return joined.length > 0 ? prefix + joined : '';
624
625
  };
625
626
 
626
- },{"./formats":1,"./utils":5,"side-channel":16}],5:[function(require,module,exports){
627
+ },{"./formats":1,"./utils":5,"side-channel":17}],5:[function(require,module,exports){
627
628
  'use strict';
628
629
 
629
630
  var formats = require('./formats');
@@ -1052,18 +1053,23 @@ var ThrowTypeError = $gOPD
1052
1053
  : throwTypeError;
1053
1054
 
1054
1055
  var hasSymbols = require('has-symbols')();
1056
+ var hasProto = require('has-proto')();
1055
1057
 
1056
- var getProto = Object.getPrototypeOf || function (x) { return x.__proto__; }; // eslint-disable-line no-proto
1058
+ var getProto = Object.getPrototypeOf || (
1059
+ hasProto
1060
+ ? function (x) { return x.__proto__; } // eslint-disable-line no-proto
1061
+ : null
1062
+ );
1057
1063
 
1058
1064
  var needsEval = {};
1059
1065
 
1060
- var TypedArray = typeof Uint8Array === 'undefined' ? undefined : getProto(Uint8Array);
1066
+ var TypedArray = typeof Uint8Array === 'undefined' || !getProto ? undefined : getProto(Uint8Array);
1061
1067
 
1062
1068
  var INTRINSICS = {
1063
1069
  '%AggregateError%': typeof AggregateError === 'undefined' ? undefined : AggregateError,
1064
1070
  '%Array%': Array,
1065
1071
  '%ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined : ArrayBuffer,
1066
- '%ArrayIteratorPrototype%': hasSymbols ? getProto([][Symbol.iterator]()) : undefined,
1072
+ '%ArrayIteratorPrototype%': hasSymbols && getProto ? getProto([][Symbol.iterator]()) : undefined,
1067
1073
  '%AsyncFromSyncIteratorPrototype%': undefined,
1068
1074
  '%AsyncFunction%': needsEval,
1069
1075
  '%AsyncGenerator%': needsEval,
@@ -1093,10 +1099,10 @@ var INTRINSICS = {
1093
1099
  '%Int32Array%': typeof Int32Array === 'undefined' ? undefined : Int32Array,
1094
1100
  '%isFinite%': isFinite,
1095
1101
  '%isNaN%': isNaN,
1096
- '%IteratorPrototype%': hasSymbols ? getProto(getProto([][Symbol.iterator]())) : undefined,
1102
+ '%IteratorPrototype%': hasSymbols && getProto ? getProto(getProto([][Symbol.iterator]())) : undefined,
1097
1103
  '%JSON%': typeof JSON === 'object' ? JSON : undefined,
1098
1104
  '%Map%': typeof Map === 'undefined' ? undefined : Map,
1099
- '%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols ? undefined : getProto(new Map()[Symbol.iterator]()),
1105
+ '%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols || !getProto ? undefined : getProto(new Map()[Symbol.iterator]()),
1100
1106
  '%Math%': Math,
1101
1107
  '%Number%': Number,
1102
1108
  '%Object%': Object,
@@ -1109,10 +1115,10 @@ var INTRINSICS = {
1109
1115
  '%Reflect%': typeof Reflect === 'undefined' ? undefined : Reflect,
1110
1116
  '%RegExp%': RegExp,
1111
1117
  '%Set%': typeof Set === 'undefined' ? undefined : Set,
1112
- '%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols ? undefined : getProto(new Set()[Symbol.iterator]()),
1118
+ '%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols || !getProto ? undefined : getProto(new Set()[Symbol.iterator]()),
1113
1119
  '%SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined : SharedArrayBuffer,
1114
1120
  '%String%': String,
1115
- '%StringIteratorPrototype%': hasSymbols ? getProto(''[Symbol.iterator]()) : undefined,
1121
+ '%StringIteratorPrototype%': hasSymbols && getProto ? getProto(''[Symbol.iterator]()) : undefined,
1116
1122
  '%Symbol%': hasSymbols ? Symbol : undefined,
1117
1123
  '%SyntaxError%': $SyntaxError,
1118
1124
  '%ThrowTypeError%': ThrowTypeError,
@@ -1128,12 +1134,14 @@ var INTRINSICS = {
1128
1134
  '%WeakSet%': typeof WeakSet === 'undefined' ? undefined : WeakSet
1129
1135
  };
1130
1136
 
1131
- try {
1132
- null.error; // eslint-disable-line no-unused-expressions
1133
- } catch (e) {
1134
- // https://github.com/tc39/proposal-shadowrealm/pull/384#issuecomment-1364264229
1135
- var errorProto = getProto(getProto(e));
1136
- INTRINSICS['%Error.prototype%'] = errorProto;
1137
+ if (getProto) {
1138
+ try {
1139
+ null.error; // eslint-disable-line no-unused-expressions
1140
+ } catch (e) {
1141
+ // https://github.com/tc39/proposal-shadowrealm/pull/384#issuecomment-1364264229
1142
+ var errorProto = getProto(getProto(e));
1143
+ INTRINSICS['%Error.prototype%'] = errorProto;
1144
+ }
1137
1145
  }
1138
1146
 
1139
1147
  var doEval = function doEval(name) {
@@ -1151,7 +1159,7 @@ var doEval = function doEval(name) {
1151
1159
  }
1152
1160
  } else if (name === '%AsyncIteratorPrototype%') {
1153
1161
  var gen = doEval('%AsyncGenerator%');
1154
- if (gen) {
1162
+ if (gen && getProto) {
1155
1163
  value = getProto(gen.prototype);
1156
1164
  }
1157
1165
  }
@@ -1352,7 +1360,20 @@ module.exports = function GetIntrinsic(name, allowMissing) {
1352
1360
  return value;
1353
1361
  };
1354
1362
 
1355
- },{"function-bind":10,"has":14,"has-symbols":12}],12:[function(require,module,exports){
1363
+ },{"function-bind":10,"has":15,"has-proto":12,"has-symbols":13}],12:[function(require,module,exports){
1364
+ 'use strict';
1365
+
1366
+ var test = {
1367
+ foo: {}
1368
+ };
1369
+
1370
+ var $Object = Object;
1371
+
1372
+ module.exports = function hasProto() {
1373
+ return { __proto__: test }.foo === test.foo && !({ __proto__: null } instanceof $Object);
1374
+ };
1375
+
1376
+ },{}],13:[function(require,module,exports){
1356
1377
  'use strict';
1357
1378
 
1358
1379
  var origSymbol = typeof Symbol !== 'undefined' && Symbol;
@@ -1367,7 +1388,7 @@ module.exports = function hasNativeSymbols() {
1367
1388
  return hasSymbolSham();
1368
1389
  };
1369
1390
 
1370
- },{"./shams":13}],13:[function(require,module,exports){
1391
+ },{"./shams":14}],14:[function(require,module,exports){
1371
1392
  'use strict';
1372
1393
 
1373
1394
  /* eslint complexity: [2, 18], max-statements: [2, 33] */
@@ -1411,14 +1432,14 @@ module.exports = function hasSymbols() {
1411
1432
  return true;
1412
1433
  };
1413
1434
 
1414
- },{}],14:[function(require,module,exports){
1435
+ },{}],15:[function(require,module,exports){
1415
1436
  'use strict';
1416
1437
 
1417
1438
  var bind = require('function-bind');
1418
1439
 
1419
1440
  module.exports = bind.call(Function.call, Object.prototype.hasOwnProperty);
1420
1441
 
1421
- },{"function-bind":10}],15:[function(require,module,exports){
1442
+ },{"function-bind":10}],16:[function(require,module,exports){
1422
1443
  var hasMap = typeof Map === 'function' && Map.prototype;
1423
1444
  var mapSizeDescriptor = Object.getOwnPropertyDescriptor && hasMap ? Object.getOwnPropertyDescriptor(Map.prototype, 'size') : null;
1424
1445
  var mapSize = hasMap && mapSizeDescriptor && typeof mapSizeDescriptor.get === 'function' ? mapSizeDescriptor.get : null;
@@ -1936,7 +1957,7 @@ function arrObjKeys(obj, inspect) {
1936
1957
  return xs;
1937
1958
  }
1938
1959
 
1939
- },{"./util.inspect":6}],16:[function(require,module,exports){
1960
+ },{"./util.inspect":6}],17:[function(require,module,exports){
1940
1961
  'use strict';
1941
1962
 
1942
1963
  var GetIntrinsic = require('get-intrinsic');
@@ -2062,5 +2083,5 @@ module.exports = function getSideChannel() {
2062
2083
  return channel;
2063
2084
  };
2064
2085
 
2065
- },{"call-bind/callBound":7,"get-intrinsic":11,"object-inspect":15}]},{},[2])(2)
2086
+ },{"call-bind/callBound":7,"get-intrinsic":11,"object-inspect":16}]},{},[2])(2)
2066
2087
  });
package/lib/parse.js CHANGED
@@ -49,7 +49,8 @@ var isoSentinel = 'utf8=%26%2310003%3B'; // encodeURIComponent('✓')
49
49
  var charsetSentinel = 'utf8=%E2%9C%93'; // encodeURIComponent('✓')
50
50
 
51
51
  var parseValues = function parseQueryStringValues(str, options) {
52
- var obj = {};
52
+ var obj = { __proto__: null };
53
+
53
54
  var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str;
54
55
  var limit = options.parameterLimit === Infinity ? undefined : options.parameterLimit;
55
56
  var parts = cleanStr.split(options.delimiter, limit);
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.11.1",
5
+ "version": "6.11.2",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/ljharb/qs.git"
@@ -40,10 +40,13 @@
40
40
  "eslint": "=8.8.0",
41
41
  "evalmd": "^0.0.19",
42
42
  "for-each": "^0.3.3",
43
+ "has-override-mistake": "^1.0.0",
44
+ "has-property-descriptors": "^1.0.0",
43
45
  "has-symbols": "^1.0.3",
44
46
  "iconv-lite": "^0.5.1",
45
47
  "in-publish": "^2.0.1",
46
48
  "mkdirp": "^0.5.5",
49
+ "mock-property": "^1.0.0",
47
50
  "npmignore": "^0.3.0",
48
51
  "nyc": "^10.3.2",
49
52
  "object-inspect": "^1.12.3",
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ emptyTestCases: [
5
+ { input: '&', withEmptyKeys: {}, stringifyOutput: '', noEmptyKeys: {} },
6
+ { input: '&&', withEmptyKeys: {}, stringifyOutput: '', noEmptyKeys: {} },
7
+ { input: '&=', withEmptyKeys: { '': '' }, stringifyOutput: '=', noEmptyKeys: {} },
8
+ { input: '&=&', withEmptyKeys: { '': '' }, stringifyOutput: '=', noEmptyKeys: {} },
9
+ { input: '&=&=', withEmptyKeys: { '': ['', ''] }, stringifyOutput: '[0]=&[1]=', noEmptyKeys: {} },
10
+ { input: '&=&=&', withEmptyKeys: { '': ['', ''] }, stringifyOutput: '[0]=&[1]=', noEmptyKeys: {} },
11
+
12
+ { input: '=', withEmptyKeys: { '': '' }, noEmptyKeys: {}, stringifyOutput: '=' },
13
+ { input: '=&', withEmptyKeys: { '': '' }, stringifyOutput: '=', noEmptyKeys: {} },
14
+ { input: '=&&&', withEmptyKeys: { '': '' }, stringifyOutput: '=', noEmptyKeys: {} },
15
+ { input: '=&=&=&', withEmptyKeys: { '': ['', '', ''] }, stringifyOutput: '[0]=&[1]=&[2]=', noEmptyKeys: {} },
16
+ { input: '=&a[]=b&a[1]=c', withEmptyKeys: { '': '', a: ['b', 'c'] }, stringifyOutput: '=&a[0]=b&a[1]=c', noEmptyKeys: { a: ['b', 'c'] } },
17
+ { input: '=a', withEmptyKeys: { '': 'a' }, noEmptyKeys: {}, stringifyOutput: '=a' },
18
+ { input: '=a', withEmptyKeys: { '': 'a' }, noEmptyKeys: {}, stringifyOutput: '=a' },
19
+ { input: 'a==a', withEmptyKeys: { a: '=a' }, noEmptyKeys: { a: '=a' }, stringifyOutput: 'a==a' },
20
+
21
+ { input: '=&a[]=b', withEmptyKeys: { '': '', a: ['b'] }, stringifyOutput: '=&a[0]=b', noEmptyKeys: { a: ['b'] } },
22
+ { input: '=&a[]=b&a[]=c&a[2]=d', withEmptyKeys: { '': '', a: ['b', 'c', 'd'] }, stringifyOutput: '=&a[0]=b&a[1]=c&a[2]=d', noEmptyKeys: { a: ['b', 'c', 'd'] } },
23
+ { input: '=a&=b', withEmptyKeys: { '': ['a', 'b'] }, stringifyOutput: '[0]=a&[1]=b', noEmptyKeys: {} },
24
+ { input: '=a&foo=b', withEmptyKeys: { '': 'a', foo: 'b' }, noEmptyKeys: { foo: 'b' }, stringifyOutput: '=a&foo=b' },
25
+
26
+ { input: 'a[]=b&a=c&=', withEmptyKeys: { '': '', a: ['b', 'c'] }, stringifyOutput: '=&a[0]=b&a[1]=c', noEmptyKeys: { a: ['b', 'c'] } },
27
+ { input: 'a[]=b&a=c&=', withEmptyKeys: { '': '', a: ['b', 'c'] }, stringifyOutput: '=&a[0]=b&a[1]=c', noEmptyKeys: { a: ['b', 'c'] } },
28
+ { input: 'a[0]=b&a=c&=', withEmptyKeys: { '': '', a: ['b', 'c'] }, stringifyOutput: '=&a[0]=b&a[1]=c', noEmptyKeys: { a: ['b', 'c'] } },
29
+ { input: 'a=b&a[]=c&=', withEmptyKeys: { '': '', a: ['b', 'c'] }, stringifyOutput: '=&a[0]=b&a[1]=c', noEmptyKeys: { a: ['b', 'c'] } },
30
+ { input: 'a=b&a[0]=c&=', withEmptyKeys: { '': '', a: ['b', 'c'] }, stringifyOutput: '=&a[0]=b&a[1]=c', noEmptyKeys: { a: ['b', 'c'] } },
31
+
32
+ { input: '[]=a&[]=b& []=1', withEmptyKeys: { '': ['a', 'b'], ' ': ['1'] }, stringifyOutput: '[0]=a&[1]=b& [0]=1', noEmptyKeys: { 0: 'a', 1: 'b', ' ': ['1'] } },
33
+ { input: '[0]=a&[1]=b&a[0]=1&a[1]=2', withEmptyKeys: { '': ['a', 'b'], a: ['1', '2'] }, noEmptyKeys: { 0: 'a', 1: 'b', a: ['1', '2'] }, stringifyOutput: '[0]=a&[1]=b&a[0]=1&a[1]=2' },
34
+ { input: '[deep]=a&[deep]=2', withEmptyKeys: { '': { deep: ['a', '2'] } }, stringifyOutput: '[deep][0]=a&[deep][1]=2', noEmptyKeys: { deep: ['a', '2'] } },
35
+ { input: '%5B0%5D=a&%5B1%5D=b', withEmptyKeys: { '': ['a', 'b'] }, stringifyOutput: '[0]=a&[1]=b', noEmptyKeys: { 0: 'a', 1: 'b' } }
36
+ ]
37
+ };
package/test/parse.js CHANGED
@@ -1,10 +1,15 @@
1
1
  'use strict';
2
2
 
3
3
  var test = require('tape');
4
- var qs = require('../');
5
- var utils = require('../lib/utils');
4
+ var hasPropertyDescriptors = require('has-property-descriptors')();
6
5
  var iconv = require('iconv-lite');
6
+ var mockProperty = require('mock-property');
7
+ var hasOverrideMistake = require('has-override-mistake')();
7
8
  var SaferBuffer = require('safer-buffer').Buffer;
9
+ var emptyTestCases = require('./empty-keys-cases').emptyTestCases;
10
+
11
+ var qs = require('../');
12
+ var utils = require('../lib/utils');
8
13
 
9
14
  test('parse()', function (t) {
10
15
  t.test('parses a simple string', function (st) {
@@ -601,6 +606,34 @@ test('parse()', function (t) {
601
606
  st.end();
602
607
  });
603
608
 
609
+ t.test('does not crash when the global Object prototype is frozen', { skip: !hasPropertyDescriptors || !hasOverrideMistake }, function (st) {
610
+ // We can't actually freeze the global Object prototype as that will interfere with other tests, and once an object is frozen, it
611
+ // can't be unfrozen. Instead, we add a new non-writable property to simulate this.
612
+ st.teardown(mockProperty(Object.prototype, 'frozenProp', { value: 'foo', nonWritable: true, nonEnumerable: true }));
613
+
614
+ st['throws'](
615
+ function () {
616
+ var obj = {};
617
+ obj.frozenProp = 'bar';
618
+ },
619
+ // node < 6 has a different error message
620
+ /^TypeError: Cannot assign to read only property 'frozenProp' of (?:object '#<Object>'|#<Object>)/,
621
+ 'regular assignment of an inherited non-writable property throws'
622
+ );
623
+
624
+ var parsed;
625
+ st.doesNotThrow(
626
+ function () {
627
+ parsed = qs.parse('frozenProp', { allowPrototypes: false });
628
+ },
629
+ 'parsing a nonwritable Object.prototype property does not throw'
630
+ );
631
+
632
+ st.deepEqual(parsed, {}, 'bare "frozenProp" results in {}');
633
+
634
+ st.end();
635
+ });
636
+
604
637
  t.test('params starting with a closing bracket', function (st) {
605
638
  st.deepEqual(qs.parse(']=toString'), { ']': 'toString' });
606
639
  st.deepEqual(qs.parse(']]=toString'), { ']]': 'toString' });
@@ -853,3 +886,13 @@ test('parse()', function (t) {
853
886
 
854
887
  t.end();
855
888
  });
889
+
890
+ test('parses empty keys', function (t) {
891
+ emptyTestCases.forEach(function (testCase) {
892
+ t.test('skips empty string key with ' + testCase.input, function (st) {
893
+ st.deepEqual(qs.parse(testCase.input), testCase.noEmptyKeys);
894
+
895
+ st.end();
896
+ });
897
+ });
898
+ });
package/test/stringify.js CHANGED
@@ -6,6 +6,7 @@ var utils = require('../lib/utils');
6
6
  var iconv = require('iconv-lite');
7
7
  var SaferBuffer = require('safer-buffer').Buffer;
8
8
  var hasSymbols = require('has-symbols');
9
+ var emptyTestCases = require('./empty-keys-cases').emptyTestCases;
9
10
  var hasBigInt = typeof BigInt === 'function';
10
11
 
11
12
  test('stringify()', function (t) {
@@ -952,3 +953,20 @@ test('stringify()', function (t) {
952
953
 
953
954
  t.end();
954
955
  });
956
+
957
+ test('stringifies empty keys', function (t) {
958
+ emptyTestCases.forEach(function (testCase) {
959
+ t.test('stringifies an object with empty string key with ' + testCase.input, function (st) {
960
+ st.deepEqual(qs.stringify(testCase.withEmptyKeys, { encode: false }), testCase.stringifyOutput);
961
+
962
+ st.end();
963
+ });
964
+ });
965
+
966
+ t.test('edge case with object/arrays', function (st) {
967
+ st.deepEqual(qs.stringify({ '': { '': [2, 3] } }, { encode: false }), '[][0]=2&[][1]=3');
968
+ st.deepEqual(qs.stringify({ '': { '': [2, 3], a: 2 } }, { encode: false }), '[][0]=2&[][1]=3&[a]=2');
969
+
970
+ st.end();
971
+ });
972
+ });