diff --git a/CHANGELOG.md b/CHANGELOG.md index 59e02653..32fca586 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +## **6.2.4** +- [Fix] `parse`: ignore `__proto__` keys (#428) +- [Fix] `utils.merge`: avoid a crash with a null target and an array source +- [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source +- [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided +- [Fix] when `parseArrays` is false, properly handle keys ending in `[]` +- [Robustness] `stringify`: avoid relying on a global `undefined` (#427) +- [Refactor] use cached `Array.isArray` +- [Docs] Clarify the need for "arrayLimit" option +- [meta] fix README.md (#399) +- [meta] Clean up license text so it’s properly detected as BSD-3-Clause +- [meta] add FUNDING.yml +- [actions] backport actions from main +- [Tests] use `safer-buffer` instead of `Buffer` constructor +- [Tests] remove nonexistent tape option +- [Dev Deps] backport from main + ## **6.2.3** - [Fix] follow `allowPrototypes` option during merge (#201, #200) - [Fix] chmod a-x diff --git a/component.json b/component.json index c1715a89..5fed22a4 100644 --- a/component.json +++ b/component.json @@ -2,7 +2,7 @@ "name": "qs", "repository": "hapijs/qs", "description": "query-string parser / stringifier with nesting support", - "version": "6.2.3", + "version": "6.2.4", "keywords": ["querystring", "query", "parser"], "main": "lib/index.js", "scripts": [ diff --git a/dist/qs.js b/dist/qs.js index bcac0f0c..c9a48916 100644 --- a/dist/qs.js +++ b/dist/qs.js @@ -1,4 +1,4 @@ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Qs = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0 && - (options.parseArrays && index <= options.arrayLimit) + if (!options.parseArrays && cleanRoot === '') { + obj = { 0: val }; + } else if ( + !isNaN(index) + && root !== cleanRoot + && String(index) === cleanRoot + && index >= 0 + && (options.parseArrays && index <= options.arrayLimit) ) { obj = []; obj[index] = parseObject(chain, val, options); - } else { + } else if (cleanRoot !== '__proto__') { obj[cleanRoot] = parseObject(chain, val, options); } } @@ -108,8 +110,7 @@ var parseKeys = function parseKeys(givenKey, val, options) { var keys = []; if (parent) { - // If we aren't using plain objects, optionally prefix keys - // that would overwrite object prototype properties + // If we aren't using plain objects, optionally prefix keys that would overwrite object prototype properties if (!options.plainObjects && has.call(Object.prototype, parent)) { if (!options.allowPrototypes) { return; @@ -203,7 +204,18 @@ var defaults = { encoder: Utils.encode }; -var stringify = function stringify(object, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots) { +var isArray = Array.isArray; +var stringify = function stringify( + object, + prefix, + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots +) { var obj = object; if (typeof filter === 'function') { obj = filter(prefix, obj); @@ -231,7 +243,7 @@ var stringify = function stringify(object, prefix, generateArrayPrefix, strictNu } var objKeys; - if (Array.isArray(filter)) { + if (isArray(filter)) { objKeys = filter; } else { var keys = Object.keys(obj); @@ -245,10 +257,30 @@ var stringify = function stringify(object, prefix, generateArrayPrefix, strictNu continue; } - if (Array.isArray(obj)) { - values = values.concat(stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots)); + if (isArray(obj)) { + values = values.concat(stringify( + obj[key], + generateArrayPrefix(prefix, key), + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots + )); } else { - values = values.concat(stringify(obj[key], prefix + (allowDots ? '.' + key : '[' + key + ']'), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots)); + values = values.concat(stringify( + obj[key], + prefix + (allowDots ? '.' + key : '[' + key + ']'), + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots + )); } } @@ -262,21 +294,22 @@ module.exports = function (object, opts) { var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling; var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : defaults.skipNulls; var encode = typeof options.encode === 'boolean' ? options.encode : defaults.encode; - var encoder = encode ? (typeof options.encoder === 'function' ? options.encoder : defaults.encoder) : null; + var encoder = encode ? typeof options.encoder === 'function' ? options.encoder : defaults.encoder : null; var sort = typeof options.sort === 'function' ? options.sort : null; var allowDots = typeof options.allowDots === 'undefined' ? false : options.allowDots; var objKeys; var filter; - if (options.encoder !== null && options.encoder !== undefined && typeof options.encoder !== 'function') { + if (options.encoder !== null && typeof options.encoder !== 'undefined' && typeof options.encoder !== 'function') { throw new TypeError('Encoder has to be a function.'); } if (typeof options.filter === 'function') { filter = options.filter; obj = filter('', obj); - } else if (Array.isArray(options.filter)) { - objKeys = filter = options.filter; + } else if (isArray(options.filter)) { + objKeys = options.filter; + filter = options.filter; } var keys = []; @@ -311,7 +344,17 @@ module.exports = function (object, opts) { continue; } - keys = keys.concat(stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots)); + keys = keys.concat(stringify( + obj[key], + key, + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots + )); } return keys.join(delimiter); @@ -332,7 +375,20 @@ var hexTable = (function () { var has = Object.prototype.hasOwnProperty; exports.arrayToObject = function (source, options) { - var obj = options.plainObjects ? Object.create(null) : {}; + var obj = options && options.plainObjects ? Object.create(null) : {}; + for (var i = 0; i < source.length; ++i) { + if (typeof source[i] !== 'undefined') { + obj[i] = source[i]; + } + } + + return obj; +}; + +var isArray = Array.isArray; + +var arrayToObject = function arrayToObject(source, options) { + var obj = options && options.plainObjects ? Object.create(null) : {}; for (var i = 0; i < source.length; ++i) { if (typeof source[i] !== 'undefined') { obj[i] = source[i]; @@ -342,16 +398,17 @@ exports.arrayToObject = function (source, options) { return obj; }; -exports.merge = function (target, source, options) { +exports.merge = function merge(target, source, options) { + /* eslint no-param-reassign: 0 */ if (!source) { return target; } if (typeof source !== 'object') { - if (Array.isArray(target)) { + if (isArray(target)) { target.push(source); - } else if (typeof target === 'object') { - if (options.plainObjects || options.allowPrototypes || !has.call(Object.prototype, source)) { + } else if (target && typeof target === 'object') { + if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) { target[source] = true; } } else { @@ -361,20 +418,36 @@ exports.merge = function (target, source, options) { return target; } - if (typeof target !== 'object') { + if (!target || typeof target !== 'object') { return [target].concat(source); } var mergeTarget = target; - if (Array.isArray(target) && !Array.isArray(source)) { - mergeTarget = exports.arrayToObject(target, options); + if (isArray(target) && !isArray(source)) { + mergeTarget = arrayToObject(target, options); + } + + if (isArray(target) && isArray(source)) { + source.forEach(function (item, i) { + if (has.call(target, i)) { + var targetItem = target[i]; + if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') { + target[i] = merge(targetItem, item, options); + } else { + target.push(item); + } + } else { + target[i] = item; + } + }); + return target; } return Object.keys(source).reduce(function (acc, key) { var value = source[key]; if (has.call(acc, key)) { - acc[key] = exports.merge(acc[key], value, options); + acc[key] = merge(acc[key], value, options); } else { acc[key] = value; } @@ -404,13 +477,13 @@ exports.encode = function (str) { var c = string.charCodeAt(i); if ( - c === 0x2D || // - - c === 0x2E || // . - c === 0x5F || // _ - c === 0x7E || // ~ - (c >= 0x30 && c <= 0x39) || // 0-9 - (c >= 0x41 && c <= 0x5A) || // a-z - (c >= 0x61 && c <= 0x7A) // A-Z + c === 0x2D // - + || c === 0x2E // . + || c === 0x5F // _ + || c === 0x7E // ~ + || (c >= 0x30 && c <= 0x39) // 0-9 + || (c >= 0x41 && c <= 0x5A) // a-z + || (c >= 0x61 && c <= 0x7A) // A-Z ) { out += string.charAt(i); continue; @@ -433,7 +506,11 @@ exports.encode = function (str) { i += 1; c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF)); - out += hexTable[0xF0 | (c >> 18)] + hexTable[0x80 | ((c >> 12) & 0x3F)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]; + /* eslint operator-linebreak: [2, "before"] */ + out += hexTable[0xF0 | (c >> 18)] + + hexTable[0x80 | ((c >> 12) & 0x3F)] + + hexTable[0x80 | ((c >> 6) & 0x3F)] + + hexTable[0x80 | (c & 0x3F)]; } return out; @@ -452,7 +529,7 @@ exports.compact = function (obj, references) { refs.push(obj); - if (Array.isArray(obj)) { + if (isArray(obj)) { var compacted = []; for (var i = 0; i < obj.length; ++i) { @@ -488,4 +565,4 @@ exports.isBuffer = function (obj) { }; },{}]},{},[1])(1) -}); \ No newline at end of file +}); diff --git a/package.json b/package.json index b248fb9d..b1dcf19e 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "qs", "description": "A querystring parser that supports nesting and arrays, with a depth limit", "homepage": "https://github.com/ljharb/qs", - "version": "6.2.3", + "version": "6.2.4", "repository": { "type": "git", "url": "https://github.com/ljharb/qs.git"