Skip to content

Conversation

@renovate
Copy link
Contributor

@renovate renovate bot commented Dec 30, 2025

This PR contains the following updates:

Package Change Age Confidence
qs 6.11.26.14.1 age confidence

GitHub Vulnerability Alerts

CVE-2025-15284

Summary

The arrayLimit option in qs does not enforce limits for bracket notation (a[]=1&a[]=2), allowing attackers to cause denial-of-service via memory exhaustion. Applications using arrayLimit for DoS protection are vulnerable.

Details

The arrayLimit option only checks limits for indexed notation (a[0]=1&a[1]=2) but completely bypasses it for bracket notation (a[]=1&a[]=2).

Vulnerable code (lib/parse.js:159-162):

if (root === '[]' && options.parseArrays) {
    obj = utils.combine([], leaf);  // No arrayLimit check
}

Working code (lib/parse.js:175):

else if (index <= options.arrayLimit) {  // Limit checked here
    obj = [];
    obj[index] = leaf;
}

The bracket notation handler at line 159 uses utils.combine([], leaf) without validating against options.arrayLimit, while indexed notation at line 175 checks index <= options.arrayLimit before creating arrays.

PoC

Test 1 - Basic bypass:

npm install qs
const qs = require('qs');
const result = qs.parse('a[]=1&a[]=2&a[]=3&a[]=4&a[]=5&a[]=6', { arrayLimit: 5 });
console.log(result.a.length);  // Output: 6 (should be max 5)

Test 2 - DoS demonstration:

const qs = require('qs');
const attack = 'a[]=' + Array(10000).fill('x').join('&a[]=');
const result = qs.parse(attack, { arrayLimit: 100 });
console.log(result.a.length);  // Output: 10000 (should be max 100)

Configuration:

  • arrayLimit: 5 (test 1) or arrayLimit: 100 (test 2)
  • Use bracket notation: a[]=value (not indexed a[0]=value)

Impact

Denial of Service via memory exhaustion. Affects applications using qs.parse() with user-controlled input and arrayLimit for protection.

Attack scenario:

  1. Attacker sends HTTP request: GET /api/search?filters[]=x&filters[]=x&...&filters[]=x (100,000+ times)
  2. Application parses with qs.parse(query, { arrayLimit: 100 })
  3. qs ignores limit, parses all 100,000 elements into array
  4. Server memory exhausted → application crashes or becomes unresponsive
  5. Service unavailable for all users

Real-world impact:

  • Single malicious request can crash server
  • No authentication required
  • Easy to automate and scale
  • Affects any endpoint parsing query strings with bracket notation

Suggested Fix

Add arrayLimit validation to the bracket notation handler. The code already calculates currentArrayLength at line 147-151, but it's not used in the bracket notation handler at line 159.

Current code (lib/parse.js:159-162):

if (root === '[]' && options.parseArrays) {
    obj = options.allowEmptyArrays && (leaf === '' || (options.strictNullHandling && leaf === null))
        ? []
        : utils.combine([], leaf);  // No arrayLimit check
}

Fixed code:

if (root === '[]' && options.parseArrays) {
    // Use currentArrayLength already calculated at line 147-151
    if (options.throwOnLimitExceeded && currentArrayLength >= options.arrayLimit) {
        throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
    }
    
    // If limit exceeded and not throwing, convert to object (consistent with indexed notation behavior)
    if (currentArrayLength >= options.arrayLimit) {
        obj = options.plainObjects ? { __proto__: null } : {};
        obj[currentArrayLength] = leaf;
    } else {
        obj = options.allowEmptyArrays && (leaf === '' || (options.strictNullHandling && leaf === null))
            ? []
            : utils.combine([], leaf);
    }
}

This makes bracket notation behaviour consistent with indexed notation, enforcing arrayLimit and converting to object when limit is exceeded (per README documentation).


Release Notes

ljharb/qs (qs)

v6.14.1

Compare Source

  • [Fix] ensure arrayLength applies to [] notation as well
  • [Fix] parse: when a custom decoder returns null for a key, ignore that key
  • [Refactor] parse: extract key segment splitting helper
  • [meta] add threat model
  • [actions] add workflow permissions
  • [Tests] stringify: increase coverage
  • [Dev Deps] update eslint, @ljharb/eslint-config, npmignore, es-value-fixtures, for-each, object-inspect

v6.14.0

Compare Source

  • [New] parse: add throwOnParameterLimitExceeded option (#​517)
  • [Refactor] parse: use utils.combine more
  • [patch] parse: add explicit throwOnLimitExceeded default
  • [actions] use shared action; re-add finishers
  • [meta] Fix changelog formatting bug
  • [Deps] update side-channel
  • [Dev Deps] update es-value-fixtures, has-bigints, has-proto, has-symbols
  • [Tests] increase coverage

v6.13.1

Compare Source

  • [Fix] stringify: avoid a crash when a filter key is null
  • [Fix] utils.merge: functions should not be stringified into keys
  • [Fix] parse: avoid a crash with interpretNumericEntities: true, comma: true, and iso charset
  • [Fix] stringify: ensure a non-string filter does not crash
  • [Refactor] use __proto__ syntax instead of Object.create for null objects
  • [Refactor] misc cleanup
  • [Tests] utils.merge: add some coverage
  • [Tests] fix a test case
  • [actions] split out node 10-20, and 20+
  • [Dev Deps] update es-value-fixtures, mock-property, object-inspect, tape

v6.13.0

Compare Source

  • [New] parse: add strictDepth option (#​511)
  • [Tests] use npm audit instead of aud

v6.12.3

Compare Source

  • [Fix] parse: properly account for strictNullHandling when allowEmptyArrays
  • [meta] fix changelog indentation

v6.12.2

Compare Source

  • [Fix] parse: parse encoded square brackets (#​506)
  • [readme] add CII best practices badge

v6.12.1

Compare Source

  • [Fix] parse: Disable decodeDotInKeys by default to restore previous behavior (#​501)
  • [Performance] utils: Optimize performance under large data volumes, reduce memory usage, and speed up processing (#​502)
  • [Refactor] utils: use +=
  • [Tests] increase coverage

v6.12.0

Compare Source

  • [New] parse/stringify: add decodeDotInKeys/encodeDotKeys options (#​488)
  • [New] parse: add duplicates option
  • [New] parse/stringify: add allowEmptyArrays option to allow [] in object values (#​487)
  • [Refactor] parse/stringify: move allowDots config logic to its own variable
  • [Refactor] stringify: move option-handling code into normalizeStringifyOptions
  • [readme] update readme, add logos (#​484)
  • [readme] stringify: clarify default arrayFormat behavior
  • [readme] fix line wrapping
  • [readme] remove dead badges
  • [Deps] update side-channel
  • [meta] make the dist build 50% smaller
  • [meta] add sideEffects flag
  • [meta] run build in prepack, not prepublish
  • [Tests] parse: remove useless tests; add coverage
  • [Tests] stringify: increase coverage
  • [Tests] use mock-property
  • [Tests] stringify: improve coverage
  • [Dev Deps] update @ljharb/eslint-config , aud, has-override-mistake, has-property-descriptors, mock-property, npmignore, object-inspect, tape
  • [Dev Deps] pin glob, since v10.3.8+ requires a broken jackspeak
  • [Dev Deps] pin jackspeak since 2.1.2+ depends on npm aliases, which kill the install process in npm < 6

Configuration

📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate bot added the dependencies Pull requests that update a dependency file label Dec 30, 2025
@renovate renovate bot requested a review from a team as a code owner December 30, 2025 23:08
@renovate renovate bot requested review from jpetto and kschelonka and removed request for a team December 30, 2025 23:08
@renovate renovate bot added the dependencies Pull requests that update a dependency file label Dec 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant