qs 6.7.3 → 6.7.4

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.
@@ -0,0 +1,79 @@
1
+ # Backport Record
2
+
3
+ Commits to backport from main (v6.14.1..v6.14.2, excluding version bump):
4
+
5
+ | # | SHA | Description |
6
+ |---|-----|-------------|
7
+ | 1 | 6744d30 | [Robustness] avoid `.push`, use `void` |
8
+ | 2 | 6bdfaf5 | [readme] replace runkit/travis CI badge with shields.io check-runs badge |
9
+ | 3 | 2a35775 | [meta] fix changelog typo |
10
+ | 4 | 1b9a8b4 | [actions] fix rebase workflow permissions |
11
+ | 5 | fbc5206 | [Fix] `parse`: fix error message |
12
+ | 6 | f6a7abf | [Fix] `parse`: enforce arrayLimit on comma-parsed values |
13
+ | 7 | febb644 | [Fix] `parse`: throw on arrayLimit exceeded with indexed notation |
14
+ | 8 | cfc108f | [Fix] arrayLimit means max count, not max index |
15
+ | 9 | 6addf8c | [Fix] `parse`: mark overflow objects for indexed notation |
16
+ | 10 | 5c308e5 | [readme] clarify parseArrays and arrayLimit documentation |
17
+ | 11 | 294db90 | [readme] document addQueryPrefix does not add ? to empty output |
18
+
19
+ ## Results
20
+
21
+ | Version | Base Tag | Base SHA | Result HEAD | Applied | Skipped |
22
+ |---------|----------|----------|-------------|---------|---------|
23
+ | 6.14 | v6.14.2 | bdcf0c7 | bdcf0c7 | all | — |
24
+ | 6.13 | v6.13.1 | f1ee037 | 625fa19 | 1,2,4,10,11 | 3,5,6,7,8,9 |
25
+ | 6.12 | v6.12.3 | f90cc35 | 8a1b294 | 1,2,4,10,11 | 3,5,6,7,8,9 |
26
+ | 6.11 | v6.11.2 | 410bdd3 | 3a5f714 | 1,2,4,10,11 | 3,5,6,7,8,9 |
27
+ | 6.10 | v6.10.5 | 95bc018 | 8189da8 | 1,2,4,10,11 | 3,5,6,7,8,9 |
28
+ | 6.9 | v6.9.7 | 4cd0032 | 6a7a3bf | 1,2,4,10,11 | 3,5,6,7,8,9 |
29
+ | 6.8 | v6.8.3 | 0db5538 | 76d53e9 | 1,2,4,10,11 | 3,5,6,7,8,9 |
30
+ | 6.7 | v6.7.3 | 834389a | 2991d8b | 1,2,4,10,11 | 3,5,6,7,8,9 |
31
+ | 6.6 | v6.6.1 | 4cc653c | 7093153 | 1,2,4,10,11 | 3,5,6,7,8,9 |
32
+ | 6.5 | v6.5.3 | 298bfa5 | 40b77c3 | 1,2,4,10,11 | 3,5,6,7,8,9 |
33
+ | 6.4 | v6.4.1 | 486aa46 | d9ffe2b | 1,2,4,10 | 3,5,6,7,8,9,11 |
34
+ | 6.3 | v6.3.3 | ff235b4 | b4824cb | 1,2,4,10 | 3,5,6,7,8,9,11 |
35
+ | 6.2 | v6.2.4 | 90d9f2b | db7b937 | 1,2,4,10 | 3,5,6,7,8,9,11 |
36
+ | 6.1 | v6.1.2 | 68ca039 | 3245e1f | 1,2,10 | 3,4,5,6,7,8,9,11 |
37
+ | 6.0 | v6.0.4 | 10233c9 | b779be4 | 1,2,10 | 3,4,5,6,7,8,9,11 |
38
+
39
+ ## Legend for Applied/Skipped columns
40
+
41
+ 1=Robustness, 2=readme badge, 3=changelog typo, 4=actions permissions,
42
+ 5=error msg fix, 6=comma arrayLimit, 7=indexed throw, 8=count semantics,
43
+ 9=mark overflow, 10=parseArrays/arrayLimit docs, 11=addQueryPrefix docs
44
+
45
+ ## Notes
46
+
47
+ - #2 (badge): v6.4+ had runkit badge replaced; v6.0-v6.3 had Travis badge replaced
48
+ - #4 (actions): v6.2-v6.10 had permissions block ADDED (not modified); v6.0-v6.1 have no rebase.yml
49
+
50
+ ## Skip Reasons
51
+
52
+ | Commit | Reason for skip |
53
+ |--------|----------------|
54
+ | 3 (2a35775) | v6.14.1-specific changelog — not applicable to other versions |
55
+ | 5 (fbc5206) | Requires `throwOnLimitExceeded` option (v6.14.0+) |
56
+ | 6 (f6a7abf) | Requires overflow system (`combine(a,b,arrayLimit,plainObjects)`) from v6.14.1 |
57
+ | 7 (febb644) | Requires `throwOnLimitExceeded` option (v6.14.0+) |
58
+ | 8 (cfc108f) | Requires overflow system from v6.14.1 |
59
+ | 9 (6addf8c) | Requires overflow system from v6.14.1 |
60
+ | 11 (294db90) | v6.4 and below lack `addQueryPrefix` in README |
61
+
62
+ ## Release Tags
63
+
64
+ | Tag | SHA | Changelog Entries |
65
+ |-----|-----|-------------------|
66
+ | v6.13.2 | d8a8ab3 | Robustness, readme #543, readme #418, readme badge, actions |
67
+ | v6.12.4 | a67173e | Robustness, readme #543, readme #418, readme badge, actions |
68
+ | v6.11.3 | 6302f35 | Robustness, readme #543, readme #418, readme badge, actions |
69
+ | v6.10.6 | 1aa4bd9 | Robustness, readme #543, readme #418, readme badge, actions |
70
+ | v6.9.8 | 479d4b1 | Robustness, readme #543, readme #418, readme badge, actions |
71
+ | v6.8.4 | 0f2b1e2 | Robustness, readme #543, readme #418, readme badge, actions |
72
+ | v6.7.4 | d38e43a | Robustness, readme #543, readme #418, readme badge, actions |
73
+ | v6.6.2 | 5e1c72c | Robustness, readme #543, readme #418, readme badge, actions |
74
+ | v6.5.4 | c190488 | Robustness, readme #543, readme #418, readme badge, actions |
75
+ | v6.4.2 | 9b50144 | Robustness, readme #543, readme badge (runkit+travis), actions |
76
+ | v6.3.4 | aa3f9f4 | Robustness, readme #543, readme badge (travis), actions |
77
+ | v6.2.5 | d68f354 | Robustness, readme #543, readme badge (travis), actions |
78
+ | v6.1.3 | cb45ba1 | Robustness, readme #543, readme badge (travis) |
79
+ | v6.0.5 | a73f299 | Robustness, readme #543, readme badge (travis) |
@@ -0,0 +1,7 @@
1
+ # GHSA-2856-wc6q-8xwc — Comment to post
2
+
3
+ This is not a vulnerability. The described behavior only modifies properties on the **result object** returned by `qs.parse()` — it does not modify `Object.prototype` or any shared state. Setting `toString` or `hasOwnProperty` on a specific result object is not prototype pollution; it's the expected outcome of parsing a key with that name.
4
+
5
+ The `allowPrototypes` option exists precisely to control whether keys that shadow `Object.prototype` properties are allowed on result objects. When `depth <= 0` and brackets are present, the prototype check is applied to the bracketed form of the key rather than the stripped form — this is a minor inconsistency in the check, but the result is equivalent to what would happen with `allowPrototypes: true`, which is an explicitly supported configuration.
6
+
7
+ Closing as not applicable.
@@ -0,0 +1,53 @@
1
+ # GHSA-6rw7-vpxm-498p — Edits to make
2
+
3
+ ## Severity
4
+ Change: High → Low
5
+
6
+ ## CVSS 3.1
7
+ Old: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H` (7.5 High)
8
+ New: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L` (3.7 Low)
9
+
10
+ Changes:
11
+ - AC: L → H (requires non-default parameterLimit config for real impact)
12
+ - A: H → L (bounded memory increase, not a crash/full DoS under defaults)
13
+
14
+ ## Description — replace with:
15
+
16
+ ### Summary
17
+
18
+ The `arrayLimit` option in qs did not enforce limits for bracket notation (`a[]=1&a[]=2`), only for indexed notation (`a[0]=1`). This is a consistency bug; `arrayLimit` should apply uniformly across all array notations.
19
+
20
+ **Note:** The default `parameterLimit` of 1000 effectively mitigates the DoS scenario originally described. With default options, bracket notation cannot produce arrays larger than `parameterLimit` regardless of `arrayLimit`, because each `a[]=value` consumes one parameter slot. The severity has been reduced accordingly.
21
+
22
+ ### Details
23
+
24
+ The `arrayLimit` option only checked limits for indexed notation (`a[0]=1&a[1]=2`) but did not enforce it for bracket notation (`a[]=1&a[]=2`).
25
+
26
+ **Vulnerable code** (`lib/parse.js:159-162`):
27
+ ```javascript
28
+ if (root === '[]' && options.parseArrays) {
29
+ obj = utils.combine([], leaf); // No arrayLimit check
30
+ }
31
+ ```
32
+
33
+ **Working code** (`lib/parse.js:175`):
34
+ ```javascript
35
+ else if (index <= options.arrayLimit) { // Limit checked here
36
+ obj = [];
37
+ obj[index] = leaf;
38
+ }
39
+ ```
40
+
41
+ ### PoC
42
+
43
+ ```javascript
44
+ const qs = require('qs');
45
+ const result = qs.parse('a[]=1&a[]=2&a[]=3&a[]=4&a[]=5&a[]=6', { arrayLimit: 5 });
46
+ console.log(result.a.length); // Output: 6 (should be max 5)
47
+ ```
48
+
49
+ **Note on parameterLimit interaction:** The original advisory's "DoS demonstration" claimed a length of 10,000, but `parameterLimit` (default: 1000) caps parsing to 1,000 parameters. With default options, the actual output is 1,000, not 10,000.
50
+
51
+ ### Impact
52
+
53
+ Consistency bug in `arrayLimit` enforcement. With default `parameterLimit`, the practical DoS risk is negligible since `parameterLimit` already caps the total number of parsed parameters (and thus array elements from bracket notation). The risk increases only when `parameterLimit` is explicitly set to a very high value.
@@ -0,0 +1,10 @@
1
+ # GHSA-w7fw-mjwx-w883 — Comment to post
2
+
3
+ This is a valid report — thank you. The comma-split code path in `parseArrayValue` returned the split array immediately, bypassing the `arrayLimit` check. Unlike the bracket notation case (GHSA-6rw7-vpxm-498p), this genuinely bypasses `parameterLimit` as well, since a single parameter like `a=x,x,x,...` produces an arbitrarily large array from one `&`-delimited parameter.
4
+
5
+ A fix has been implemented: after the comma split and decode step, if the resulting array exceeds `arrayLimit`, it is converted to an object (consistent with other `arrayLimit` exceedances), and `throwOnLimitExceeded` is respected.
6
+
7
+ A few corrections to the advisory:
8
+ - **Severity should be Medium, not High**: This requires the non-default `comma: true` option to be explicitly enabled. HTTP server request size limits also bound the practical impact.
9
+ - **Vulnerable version range**: Should be `<= 6.14.1` (not `< 6.14.1`), since 6.14.1 is also affected.
10
+ - **Patched version**: Will be set once the fix is released.
@@ -0,0 +1,36 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm run lint:*)",
5
+ "Bash(npm test:*)",
6
+ "Bash(npm run tests-only:*)",
7
+ "Bash(npm show:*)",
8
+ "Bash(git log:*)",
9
+ "Bash(git tag:*)",
10
+ "Bash(npm view:*)",
11
+ "Bash(git fetch:*)",
12
+ "Bash(git cat-file:*)",
13
+ "Bash(git show:*)",
14
+ "Bash(git rev-parse:*)",
15
+ "Bash(git rev-list:*)",
16
+ "Bash(gh api:*)",
17
+ "Bash(node /tmp/qs-vuln-test.js:*)",
18
+ "Bash(git -C /Users/ljharb/git/ljharb/qs.git show 3086902 --stat)",
19
+ "Bash(git -C /Users/ljharb/git/ljharb/qs.git diff HEAD -- lib/parse.js lib/utils.js)",
20
+ "Bash(open:*)",
21
+ "WebSearch",
22
+ "WebFetch(domain:www.first.org)",
23
+ "WebFetch(domain:nvd.nist.gov)",
24
+ "WebFetch(domain:raw.githubusercontent.com)",
25
+ "Bash(git -C /Users/ljharb/git/ljharb/qs.git diff origin/main...HEAD)",
26
+ "Bash(for ver in v6.13.1 v6.12.3 v6.11.2 v6.10.5 v6.9.7 v6.8.3 v6.7.3 v6.6.1 v6.5.3 v6.4.1 v6.3.3 v6.2.4 v6.1.2 v6.0.4)",
27
+ "Bash(do echo '=== $ver ===')",
28
+ "Bash(done)",
29
+ "Bash(git checkout:*)",
30
+ "Bash(cd /Users/ljharb/git/ljharb/qs.git git add README.md git commit --no-verify --no-gpg-sign -m \"[readme] replace travis CI badge with shields.io check-runs badge\")",
31
+ "Bash(npm run dist:*)",
32
+ "Bash(for tag in v6.13.2 v6.12.4 v6.11.3 v6.10.6 v6.9.8 v6.8.4 v6.7.4 v6.6.2 v6.5.4 v6.4.2 v6.3.4 v6.2.5 v6.1.3 v6.0.5)",
33
+ "Bash(gh run view:*)"
34
+ ]
35
+ }
36
+ }
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## **6.7.4**
2
+ - [Robustness] avoid `.push`, use `void`
3
+ - [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
4
+ - [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
5
+ - [readme] replace runkit CI badge with shields.io check-runs badge
6
+ - [actions] fix rebase workflow permissions
7
+
1
8
  ## **6.7.3**
2
9
  - [Fix] `parse`: ignore `__proto__` keys (#428)
3
10
  - [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
package/README.md CHANGED
@@ -238,7 +238,7 @@ var withIndexedEmptyString = qs.parse('a[0]=b&a[1]=&a[2]=c');
238
238
  assert.deepEqual(withIndexedEmptyString, { a: ['b', '', 'c'] });
239
239
  ```
240
240
 
241
- **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
241
+ **qs** will also limit arrays to a maximum of `20` elements. Any array members with an index of `20` or greater will
242
242
  instead be converted to an object with the index as the key. This is needed to handle cases when someone sent, for example, `a[999999999]` and it will take significant time to iterate over this huge array.
243
243
 
244
244
  ```javascript
@@ -253,7 +253,7 @@ var withArrayLimit = qs.parse('a[1]=b', { arrayLimit: 0 });
253
253
  assert.deepEqual(withArrayLimit, { a: { '1': 'b' } });
254
254
  ```
255
255
 
256
- To disable array parsing entirely, set `parseArrays` to `false`.
256
+ To prevent array syntax (`a[]`, `a[0]`) from being parsed as arrays, set `parseArrays` to `false`.
257
257
 
258
258
  ```javascript
259
259
  var noParsingArrays = qs.parse('a[]=b', { parseArrays: false });
@@ -437,6 +437,12 @@ The query string may optionally be prepended with a question mark:
437
437
  assert.equal(qs.stringify({ a: 'b', c: 'd' }, { addQueryPrefix: true }), '?a=b&c=d');
438
438
  ```
439
439
 
440
+ Note that when the output is an empty string, the prefix will not be added:
441
+
442
+ ```javascript
443
+ assert.equal(qs.stringify({}, { addQueryPrefix: true }), '');
444
+ ```
445
+
440
446
  The delimiter may be overridden with stringify as well:
441
447
 
442
448
  ```javascript
@@ -612,5 +618,5 @@ The maintainers of qs and thousands of other packages are working with Tidelift
612
618
  [downloads-url]: https://npm-stat.com/charts.html?package=qs
613
619
  [codecov-image]: https://codecov.io/gh/ljharb/qs/branch/main/graphs/badge.svg
614
620
  [codecov-url]: https://app.codecov.io/gh/ljharb/qs/
615
- [actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/qs
621
+ [actions-image]: https://img.shields.io/github/check-runs/ljharb/qs/main
616
622
  [actions-url]: https://github.com/ljharb/qs/actions
package/dist/qs.js CHANGED
@@ -83,7 +83,7 @@ var maybeMap = function maybeMap(val, fn) {
83
83
  if (isArray(val)) {
84
84
  var mapped = [];
85
85
  for (var i = 0; i < val.length; i += 1) {
86
- mapped.push(fn(val[i]));
86
+ mapped[mapped.length] = fn(val[i]);
87
87
  }
88
88
  return mapped;
89
89
  }
@@ -103,7 +103,7 @@ var charsetSentinel = 'utf8=%E2%9C%93'; // encodeURIComponent('✓')
103
103
  var parseValues = function parseQueryStringValues(str, options) {
104
104
  var obj = {};
105
105
  var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str;
106
- var limit = options.parameterLimit === Infinity ? undefined : options.parameterLimit;
106
+ var limit = options.parameterLimit === Infinity ? void undefined : options.parameterLimit;
107
107
  var parts = cleanStr.split(options.delimiter, limit);
108
108
  var skipIndex = -1; // Keep track of where the utf8 sentinel was found
109
109
  var i;
@@ -228,7 +228,7 @@ var parseKeys = function parseQueryStringKeys(givenKey, val, options, valuesPars
228
228
  }
229
229
  }
230
230
 
231
- keys.push(parent);
231
+ keys[keys.length] = parent;
232
232
  }
233
233
 
234
234
  // Loop through children appending to the array until we hit depth
@@ -241,13 +241,13 @@ var parseKeys = function parseQueryStringKeys(givenKey, val, options, valuesPars
241
241
  return;
242
242
  }
243
243
  }
244
- keys.push(segment[1]);
244
+ keys[keys.length] = segment[1];
245
245
  }
246
246
 
247
247
  // If there's a remainder, just add whatever is left
248
248
 
249
249
  if (segment) {
250
- keys.push('[' + key.slice(segment.index) + ']');
250
+ keys[keys.length] = '[' + key.slice(segment.index + ']');
251
251
  }
252
252
 
253
253
  return parseObject(keys, val, options, valuesParsed);
@@ -600,7 +600,7 @@ var isArray = Array.isArray;
600
600
  var hexTable = (function () {
601
601
  var array = [];
602
602
  for (var i = 0; i < 256; ++i) {
603
- array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase());
603
+ array[array.length] = '%' + ((i < 16 ? '0' : '' + i.toString(16)).toUpperCase());
604
604
  }
605
605
 
606
606
  return array;
@@ -616,7 +616,7 @@ var compactQueue = function compactQueue(queue) {
616
616
 
617
617
  for (var j = 0; j < obj.length; ++j) {
618
618
  if (typeof obj[j] !== 'undefined') {
619
- compacted.push(obj[j]);
619
+ compacted[compacted.length] = obj[j];
620
620
  }
621
621
  }
622
622
 
@@ -643,7 +643,7 @@ var merge = function merge(target, source, options) {
643
643
 
644
644
  if (typeof source !== 'object') {
645
645
  if (isArray(target)) {
646
- target.push(source);
646
+ target[target.length] = source;
647
647
  } else if (target && typeof target === 'object') {
648
648
  if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) {
649
649
  target[source] = true; // eslint-disable-line no-param-reassign
@@ -671,7 +671,7 @@ var merge = function merge(target, source, options) {
671
671
  if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') {
672
672
  target[i] = merge(targetItem, item, options); // eslint-disable-line no-param-reassign
673
673
  } else {
674
- target.push(item);
674
+ target[target.length] = item;
675
675
  }
676
676
  } else {
677
677
  target[i] = item; // eslint-disable-line no-param-reassign
@@ -785,8 +785,8 @@ var compact = function compact(value) {
785
785
  var key = keys[j];
786
786
  var val = obj[key];
787
787
  if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) {
788
- queue.push({ obj: obj, prop: key });
789
- refs.push(val);
788
+ queue[queue.length] = { obj: obj, prop: key };
789
+ refs[refs.length] = val;
790
790
  }
791
791
  }
792
792
  }
package/lib/parse.js CHANGED
@@ -41,7 +41,7 @@ var maybeMap = function maybeMap(val, fn) {
41
41
  if (isArray(val)) {
42
42
  var mapped = [];
43
43
  for (var i = 0; i < val.length; i += 1) {
44
- mapped.push(fn(val[i]));
44
+ mapped[mapped.length] = fn(val[i]);
45
45
  }
46
46
  return mapped;
47
47
  }
@@ -61,7 +61,7 @@ var charsetSentinel = 'utf8=%E2%9C%93'; // encodeURIComponent('✓')
61
61
  var parseValues = function parseQueryStringValues(str, options) {
62
62
  var obj = {};
63
63
  var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str;
64
- var limit = options.parameterLimit === Infinity ? undefined : options.parameterLimit;
64
+ var limit = options.parameterLimit === Infinity ? void undefined : options.parameterLimit;
65
65
  var parts = cleanStr.split(options.delimiter, limit);
66
66
  var skipIndex = -1; // Keep track of where the utf8 sentinel was found
67
67
  var i;
@@ -186,7 +186,7 @@ var parseKeys = function parseQueryStringKeys(givenKey, val, options, valuesPars
186
186
  }
187
187
  }
188
188
 
189
- keys.push(parent);
189
+ keys[keys.length] = parent;
190
190
  }
191
191
 
192
192
  // Loop through children appending to the array until we hit depth
@@ -199,13 +199,13 @@ var parseKeys = function parseQueryStringKeys(givenKey, val, options, valuesPars
199
199
  return;
200
200
  }
201
201
  }
202
- keys.push(segment[1]);
202
+ keys[keys.length] = segment[1];
203
203
  }
204
204
 
205
205
  // If there's a remainder, just add whatever is left
206
206
 
207
207
  if (segment) {
208
- keys.push('[' + key.slice(segment.index) + ']');
208
+ keys[keys.length] = '[' + key.slice(segment.index + ']');
209
209
  }
210
210
 
211
211
  return parseObject(keys, val, options, valuesParsed);
package/lib/utils.js CHANGED
@@ -6,7 +6,7 @@ var isArray = Array.isArray;
6
6
  var hexTable = (function () {
7
7
  var array = [];
8
8
  for (var i = 0; i < 256; ++i) {
9
- array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase());
9
+ array[array.length] = '%' + ((i < 16 ? '0' : '' + i.toString(16)).toUpperCase());
10
10
  }
11
11
 
12
12
  return array;
@@ -22,7 +22,7 @@ var compactQueue = function compactQueue(queue) {
22
22
 
23
23
  for (var j = 0; j < obj.length; ++j) {
24
24
  if (typeof obj[j] !== 'undefined') {
25
- compacted.push(obj[j]);
25
+ compacted[compacted.length] = obj[j];
26
26
  }
27
27
  }
28
28
 
@@ -49,7 +49,7 @@ var merge = function merge(target, source, options) {
49
49
 
50
50
  if (typeof source !== 'object') {
51
51
  if (isArray(target)) {
52
- target.push(source);
52
+ target[target.length] = source;
53
53
  } else if (target && typeof target === 'object') {
54
54
  if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) {
55
55
  target[source] = true; // eslint-disable-line no-param-reassign
@@ -77,7 +77,7 @@ var merge = function merge(target, source, options) {
77
77
  if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') {
78
78
  target[i] = merge(targetItem, item, options); // eslint-disable-line no-param-reassign
79
79
  } else {
80
- target.push(item);
80
+ target[target.length] = item;
81
81
  }
82
82
  } else {
83
83
  target[i] = item; // eslint-disable-line no-param-reassign
@@ -191,8 +191,8 @@ var compact = function compact(value) {
191
191
  var key = keys[j];
192
192
  var val = obj[key];
193
193
  if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) {
194
- queue.push({ obj: obj, prop: key });
195
- refs.push(val);
194
+ queue[queue.length] = { obj: obj, prop: key };
195
+ refs[refs.length] = val;
196
196
  }
197
197
  }
198
198
  }
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.7.3",
5
+ "version": "6.7.4",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/ljharb/qs.git"