@@ -235,11 +235,11 @@ test('parse()', function (t) {
235235 st . deepEqual ( qs . parse ( 'a=b&a[0]=c' ) , { a : [ 'b' , 'c' ] } ) ;
236236
237237 st . deepEqual ( qs . parse ( 'a[1]=b&a=c' , { arrayLimit : 20 } ) , { a : [ 'b' , 'c' ] } ) ;
238- st . deepEqual ( qs . parse ( 'a[]=b&a=c' , { arrayLimit : 0 } ) , { a : [ 'b' , 'c' ] } ) ;
238+ st . deepEqual ( qs . parse ( 'a[]=b&a=c' , { arrayLimit : 0 } ) , { a : { 0 : 'b' , 1 : 'c' } } ) ;
239239 st . deepEqual ( qs . parse ( 'a[]=b&a=c' ) , { a : [ 'b' , 'c' ] } ) ;
240240
241241 st . deepEqual ( qs . parse ( 'a=b&a[1]=c' , { arrayLimit : 20 } ) , { a : [ 'b' , 'c' ] } ) ;
242- st . deepEqual ( qs . parse ( 'a=b&a[]=c' , { arrayLimit : 0 } ) , { a : [ 'b' , 'c' ] } ) ;
242+ st . deepEqual ( qs . parse ( 'a=b&a[]=c' , { arrayLimit : 0 } ) , { a : { 0 : 'b' , 1 : 'c' } } ) ;
243243 st . deepEqual ( qs . parse ( 'a=b&a[]=c' ) , { a : [ 'b' , 'c' ] } ) ;
244244
245245 st . end ( ) ;
@@ -364,7 +364,7 @@ test('parse()', function (t) {
364364 ) ;
365365 st . deepEqual (
366366 qs . parse ( 'a[]=b&a[]&a[]=c&a[]=' , { strictNullHandling : true , arrayLimit : 0 } ) ,
367- { a : [ 'b' , null , 'c' , '' ] } ,
367+ { a : { 0 : 'b' , 1 : null , 2 : 'c' , 3 : '' } } ,
368368 'with arrayLimit 0 + array brackets: null then empty string works'
369369 ) ;
370370
@@ -375,7 +375,7 @@ test('parse()', function (t) {
375375 ) ;
376376 st . deepEqual (
377377 qs . parse ( 'a[]=b&a[]=&a[]=c&a[]' , { strictNullHandling : true , arrayLimit : 0 } ) ,
378- { a : [ 'b' , '' , 'c' , null ] } ,
378+ { a : { 0 : 'b' , 1 : '' , 2 : 'c' , 3 : null } } ,
379379 'with arrayLimit 0 + array brackets: empty string then null works'
380380 ) ;
381381
@@ -1288,3 +1288,109 @@ test('qs strictDepth option - non-throw cases', function (t) {
12881288 st . end ( ) ;
12891289 } ) ;
12901290} ) ;
1291+
1292+ test ( 'DOS' , function ( t ) {
1293+ var arr = [ ] ;
1294+ for ( var i = 0 ; i < 105 ; i ++ ) {
1295+ arr [ arr . length ] = 'x' ;
1296+ }
1297+ var attack = 'a[]=' + arr . join ( '&a[]=' ) ;
1298+ var result = qs . parse ( attack , { arrayLimit : 100 } ) ;
1299+
1300+ t . notOk ( Array . isArray ( result . a ) , 'arrayLimit is respected: result is an object, not an array' ) ;
1301+ t . equal ( Object . keys ( result . a ) . length , 105 , 'all values are preserved' ) ;
1302+
1303+ t . end ( ) ;
1304+ } ) ;
1305+
1306+ test ( 'arrayLimit boundary conditions' , function ( t ) {
1307+ t . test ( 'exactly at the limit stays as array' , function ( st ) {
1308+ var result = qs . parse ( 'a[]=1&a[]=2&a[]=3' , { arrayLimit : 3 } ) ;
1309+ st . ok ( Array . isArray ( result . a ) , 'result is an array when exactly at limit' ) ;
1310+ st . deepEqual ( result . a , [ '1' , '2' , '3' ] , 'all values present' ) ;
1311+ st . end ( ) ;
1312+ } ) ;
1313+
1314+ t . test ( 'one over the limit converts to object' , function ( st ) {
1315+ var result = qs . parse ( 'a[]=1&a[]=2&a[]=3&a[]=4' , { arrayLimit : 3 } ) ;
1316+ st . notOk ( Array . isArray ( result . a ) , 'result is not an array when over limit' ) ;
1317+ st . deepEqual ( result . a , { 0 : '1' , 1 : '2' , 2 : '3' , 3 : '4' } , 'all values preserved as object' ) ;
1318+ st . end ( ) ;
1319+ } ) ;
1320+
1321+ t . test ( 'arrayLimit 1 with two values' , function ( st ) {
1322+ var result = qs . parse ( 'a[]=1&a[]=2' , { arrayLimit : 1 } ) ;
1323+ st . notOk ( Array . isArray ( result . a ) , 'result is not an array' ) ;
1324+ st . deepEqual ( result . a , { 0 : '1' , 1 : '2' } , 'both values preserved' ) ;
1325+ st . end ( ) ;
1326+ } ) ;
1327+
1328+ t . end ( ) ;
1329+ } ) ;
1330+
1331+ test ( 'mixed array and object notation' , function ( t ) {
1332+ t . test ( 'array brackets with object key - under limit' , function ( st ) {
1333+ st . deepEqual (
1334+ qs . parse ( 'a[]=b&a[c]=d' ) ,
1335+ { a : { 0 : 'b' , c : 'd' } } ,
1336+ 'mixing [] and [key] converts to object'
1337+ ) ;
1338+ st . end ( ) ;
1339+ } ) ;
1340+
1341+ t . test ( 'array index with object key - under limit' , function ( st ) {
1342+ st . deepEqual (
1343+ qs . parse ( 'a[0]=b&a[c]=d' ) ,
1344+ { a : { 0 : 'b' , c : 'd' } } ,
1345+ 'mixing [0] and [key] produces object'
1346+ ) ;
1347+ st . end ( ) ;
1348+ } ) ;
1349+
1350+ t . test ( 'plain value with array brackets - under limit' , function ( st ) {
1351+ st . deepEqual (
1352+ qs . parse ( 'a=b&a[]=c' , { arrayLimit : 20 } ) ,
1353+ { a : [ 'b' , 'c' ] } ,
1354+ 'plain value combined with [] stays as array under limit'
1355+ ) ;
1356+ st . end ( ) ;
1357+ } ) ;
1358+
1359+ t . test ( 'array brackets with plain value - under limit' , function ( st ) {
1360+ st . deepEqual (
1361+ qs . parse ( 'a[]=b&a=c' , { arrayLimit : 20 } ) ,
1362+ { a : [ 'b' , 'c' ] } ,
1363+ '[] combined with plain value stays as array under limit'
1364+ ) ;
1365+ st . end ( ) ;
1366+ } ) ;
1367+
1368+ t . test ( 'plain value with array index - under limit' , function ( st ) {
1369+ st . deepEqual (
1370+ qs . parse ( 'a=b&a[0]=c' , { arrayLimit : 20 } ) ,
1371+ { a : [ 'b' , 'c' ] } ,
1372+ 'plain value combined with [0] stays as array under limit'
1373+ ) ;
1374+ st . end ( ) ;
1375+ } ) ;
1376+
1377+ t . test ( 'multiple plain values with duplicates combine' , function ( st ) {
1378+ st . deepEqual (
1379+ qs . parse ( 'a=b&a=c&a=d' , { arrayLimit : 20 } ) ,
1380+ { a : [ 'b' , 'c' , 'd' ] } ,
1381+ 'duplicate plain keys combine into array'
1382+ ) ;
1383+ st . end ( ) ;
1384+ } ) ;
1385+
1386+ t . test ( 'multiple plain values exceeding limit' , function ( st ) {
1387+ st . deepEqual (
1388+ qs . parse ( 'a=b&a=c&a=d' , { arrayLimit : 2 } ) ,
1389+ { a : { 0 : 'b' , 1 : 'c' , 2 : 'd' } } ,
1390+ 'duplicate plain keys convert to object when exceeding limit'
1391+ ) ;
1392+ st . end ( ) ;
1393+ } ) ;
1394+
1395+ t . end ( ) ;
1396+ } ) ;
0 commit comments