Cypress version 13.14.0 represents a minor update over its predecessor, 13.13.3, focusing primarily on stability and incremental improvements rather than introducing groundbreaking new features. Both versions share the same core dependencies, indicating a consistent technological foundation. Developers can expect familiar functionality and API usage in the newer version, ensuring a smooth transition for existing projects.
A key difference lies in the increased "unpackedSize" of 13.14.0 (7343609 bytes) compared to 13.13.3 (7342866 bytes), suggesting potential adjustments in internal assets, documentation updates, or minor code additions. While the dependency list remains identical, including crucial packages like lodash, bluebird, and @cypress/request, this size difference hints at subtle under-the-hood changes that might address bug fixes or optimize performance.
The release date difference also highlights the recency of version 13.14.0 (August 27, 2024) compared to 13.13.3 (August 14, 2024). Cypress users should upgrade to the latest version to benefit from the most recent bug fixes and potential performance enhancements. Staying current guarantees compatibility with the latest web standards and browser versions, securing applications from emerging risks. While the impact of these version updates may be limited for most users, those prioritizing maximum application stability should update.
All the vulnerabilities related to the version 13.14.0 of the package
qs's arrayLimit bypass in its bracket notation allows DoS via memory exhaustion
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.
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.
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)a[]=value (not indexed a[0]=value)Denial of Service via memory exhaustion. Affects applications using qs.parse() with user-controlled input and arrayLimit for protection.
Attack scenario:
GET /api/search?filters[]=x&filters[]=x&...&filters[]=x (100,000+ times)qs.parse(query, { arrayLimit: 100 })Real-world impact:
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).