@@ -262,6 +262,188 @@ withVersions('fastify', 'fastify', version => {
262
262
}
263
263
} )
264
264
} )
265
+
266
+ describe ( 'Suspicious request blocking - path parameters' , ( ) => {
267
+ // Skip Fastify v1 - preValidation hook is not supported
268
+ if ( semver . lt ( semver . coerce ( version ) , '2.0.0' ) ) {
269
+ return
270
+ }
271
+
272
+ let server , preHandlerHookSpy , preValidationHookSpy , axios
273
+
274
+ before ( ( ) => {
275
+ return agent . load ( [ 'fastify' , 'http' ] , { client : false } )
276
+ } )
277
+
278
+ before ( ( done ) => {
279
+ const fastify = require ( `../../../../versions/fastify@${ version } ` ) . get ( )
280
+
281
+ const app = fastify ( )
282
+ app . get ( '/multiple-path-params/:parameter1/:parameter2' , ( request , reply ) => {
283
+ reply . send ( 'DONE' )
284
+ } )
285
+
286
+ app . register ( async function ( nested ) {
287
+ nested . get ( '/:nestedParam' , async ( request , reply ) => {
288
+ reply . send ( 'DONE' )
289
+ } )
290
+ } , { prefix : '/nested/:parentParam' } )
291
+
292
+ const paramHook = ( request , reply , done ) => {
293
+ done ( )
294
+ }
295
+
296
+ preHandlerHookSpy = sinon . spy ( paramHook )
297
+
298
+ app . addHook ( 'preHandler' , preHandlerHookSpy )
299
+
300
+ const validationHook = ( request , reply , done ) => {
301
+ done ( )
302
+ }
303
+
304
+ preValidationHookSpy = sinon . spy ( validationHook )
305
+ app . addHook ( 'preValidation' , preValidationHookSpy )
306
+
307
+ app . get ( '/callback-path-param/:pathParameter' , ( request , reply ) => {
308
+ reply . send ( 'DONE' )
309
+ } )
310
+
311
+ getPort ( ) . then ( ( port ) => {
312
+ app . listen ( { port } , ( ) => {
313
+ axios = Axios . create ( { baseURL : `http://localhost:${ port } ` } )
314
+ done ( )
315
+ } )
316
+ server = app . server
317
+ } )
318
+ } )
319
+
320
+ after ( ( ) => {
321
+ server . close ( )
322
+ return agent . close ( { ritmReset : false } )
323
+ } )
324
+
325
+ beforeEach ( async ( ) => {
326
+ appsec . enable ( new Config ( {
327
+ appsec : {
328
+ enabled : true ,
329
+ rules : path . join ( __dirname , 'rules-example.json' )
330
+ }
331
+ } ) )
332
+ } )
333
+
334
+ afterEach ( ( ) => {
335
+ appsec . disable ( )
336
+ sinon . reset ( )
337
+ } )
338
+
339
+ describe ( 'route with multiple path parameters' , ( ) => {
340
+ it ( 'should not block the request when attack is not detected' , async ( ) => {
341
+ const res = await axios . get ( '/multiple-path-params/safe_param/safe_param' )
342
+
343
+ assert . equal ( res . status , 200 )
344
+ assert . equal ( res . data , 'DONE' )
345
+ } )
346
+
347
+ it ( 'should block the request when attack is detected in both parameters' , async ( ) => {
348
+ try {
349
+ await axios . get ( '/multiple-path-params/testattack/testattack' )
350
+
351
+ return Promise . reject ( new Error ( 'Request should not return 200' ) )
352
+ } catch ( e ) {
353
+ assert . equal ( e . response . status , 403 )
354
+ assert . deepEqual ( e . response . data , JSON . parse ( json ) )
355
+ }
356
+ } )
357
+
358
+ it ( 'should block the request when attack is detected in the first parameter' , async ( ) => {
359
+ try {
360
+ await axios . get ( '/multiple-path-params/testattack/safe_param' )
361
+
362
+ return Promise . reject ( new Error ( 'Request should not return 200' ) )
363
+ } catch ( e ) {
364
+ assert . equal ( e . response . status , 403 )
365
+ assert . deepEqual ( e . response . data , JSON . parse ( json ) )
366
+ }
367
+ } )
368
+
369
+ it ( 'should block the request when attack is detected in the second parameter' , async ( ) => {
370
+ try {
371
+ await axios . get ( '/multiple-path-params/safe_param/testattack' )
372
+
373
+ return Promise . reject ( new Error ( 'Request should not return 200' ) )
374
+ } catch ( e ) {
375
+ assert . equal ( e . response . status , 403 )
376
+ assert . deepEqual ( e . response . data , JSON . parse ( json ) )
377
+ }
378
+ } )
379
+ } )
380
+
381
+ describe ( 'nested routes' , ( ) => {
382
+ it ( 'should not block the request when attack is not detected' , async ( ) => {
383
+ const res = await axios . get ( '/nested/safe_param/safe_param' )
384
+
385
+ assert . equal ( res . status , 200 )
386
+ assert . equal ( res . data , 'DONE' )
387
+ } )
388
+
389
+ it ( 'should block the request when attack is detected in the nested parameter' , async ( ) => {
390
+ try {
391
+ await axios . get ( '/nested/safe_param/testattack' )
392
+
393
+ return Promise . reject ( new Error ( 'Request should not return 200' ) )
394
+ } catch ( e ) {
395
+ assert . equal ( e . response . status , 403 )
396
+ assert . deepEqual ( e . response . data , JSON . parse ( json ) )
397
+ }
398
+ } )
399
+
400
+ it ( 'should block the request when attack is detected in the parent parameter' , async ( ) => {
401
+ try {
402
+ await axios . get ( '/nested/testattack/safe_param' )
403
+
404
+ return Promise . reject ( new Error ( 'Request should not return 200' ) )
405
+ } catch ( e ) {
406
+ assert . equal ( e . response . status , 403 )
407
+ assert . deepEqual ( e . response . data , JSON . parse ( json ) )
408
+ }
409
+ } )
410
+
411
+ it ( 'should block the request when attack is detected in both parameters' , async ( ) => {
412
+ try {
413
+ await axios . get ( '/nested/testattack/testattack' )
414
+
415
+ return Promise . reject ( new Error ( 'Request should not return 200' ) )
416
+ } catch ( e ) {
417
+ assert . equal ( e . response . status , 403 )
418
+ assert . deepEqual ( e . response . data , JSON . parse ( json ) )
419
+ }
420
+ } )
421
+ } )
422
+
423
+ describe ( 'path parameter with hook' , ( ) => {
424
+ it ( 'should not block the request when attack is not detected' , async ( ) => {
425
+ const res = await axios . get ( '/callback-path-param/safe_param' )
426
+
427
+ assert . equal ( res . status , 200 )
428
+ assert . equal ( res . data , 'DONE' )
429
+ sinon . assert . calledOnce ( preHandlerHookSpy )
430
+ sinon . assert . calledOnce ( preValidationHookSpy )
431
+ } )
432
+
433
+ it ( 'should block the request when attack is detected' , async ( ) => {
434
+ try {
435
+ await axios . get ( '/callback-path-param/testattack' )
436
+
437
+ return Promise . reject ( new Error ( 'Request should not return 200' ) )
438
+ } catch ( e ) {
439
+ assert . equal ( e . response . status , 403 )
440
+ assert . deepEqual ( e . response . data , JSON . parse ( json ) )
441
+ sinon . assert . notCalled ( preHandlerHookSpy )
442
+ sinon . assert . notCalled ( preValidationHookSpy )
443
+ }
444
+ } )
445
+ } )
446
+ } )
265
447
} )
266
448
267
449
const createNestedObject = ( n , obj ) => {
0 commit comments