@@ -529,5 +529,136 @@ describe('ParseGraphQL Query Complexity', () => {
529529 expect ( result . data . users ) . toBeDefined ( ) ;
530530 } ) ;
531531 } ) ;
532+
533+ describe ( 'Multi-operation document handling (Security)' , ( ) => {
534+ it ( 'should validate the correct operation when multiple operations are in document' , async ( ) => {
535+ await reconfigureServer ( {
536+ maxGraphQLQueryComplexity : {
537+ fields : 4 ,
538+ } ,
539+ } ) ;
540+
541+ // Document with two operations: one simple, one complex
542+ const query = `
543+ query SimpleQuery {
544+ users {
545+ edges {
546+ node {
547+ objectId
548+ }
549+ }
550+ }
551+ }
552+
553+ query ComplexQuery {
554+ users {
555+ edges {
556+ node {
557+ objectId
558+ username
559+ createdAt
560+ updatedAt
561+ email
562+ }
563+ }
564+ }
565+ }
566+ ` ;
567+
568+ // SimpleQuery should pass (4 fields: users, edges, node, objectId)
569+ const simpleResponse = await fetch ( 'http://localhost:13378/graphql' , {
570+ method : 'POST' ,
571+ headers : {
572+ 'Content-Type' : 'application/json' ,
573+ 'X-Parse-Application-Id' : 'test' ,
574+ 'X-Parse-Javascript-Key' : 'test' ,
575+ } ,
576+ body : JSON . stringify ( {
577+ query,
578+ operationName : 'SimpleQuery'
579+ } )
580+ } ) ;
581+ const simpleResult = await simpleResponse . json ( ) ;
582+ expect ( simpleResult . data . users ) . toBeDefined ( ) ;
583+
584+ // ComplexQuery should fail (8 fields > 4 limit)
585+ const complexResponse = await fetch ( 'http://localhost:13378/graphql' , {
586+ method : 'POST' ,
587+ headers : {
588+ 'Content-Type' : 'application/json' ,
589+ 'X-Parse-Application-Id' : 'test' ,
590+ 'X-Parse-Javascript-Key' : 'test' ,
591+ } ,
592+ body : JSON . stringify ( {
593+ query,
594+ operationName : 'ComplexQuery'
595+ } )
596+ } ) ;
597+ const complexResult = await complexResponse . json ( ) ;
598+ expect ( complexResult . errors ) . toBeDefined ( ) ;
599+ expect ( complexResult . errors [ 0 ] . message ) . toContain ( 'Number of fields selected exceeds maximum allowed' ) ;
600+ } ) ;
601+
602+ it ( 'should block complex operation even when simple operation is first in document' , async ( ) => {
603+ await reconfigureServer ( {
604+ maxGraphQLQueryComplexity : {
605+ depth : 2 ,
606+ } ,
607+ } ) ;
608+
609+ // First operation is simple (within limits), second is complex (exceeds limits)
610+ const query = `
611+ query ShallowQuery {
612+ users {
613+ count
614+ }
615+ }
616+
617+ query DeepQuery {
618+ users {
619+ edges {
620+ node {
621+ objectId
622+ username
623+ }
624+ }
625+ }
626+ }
627+ ` ;
628+
629+ // ShallowQuery should pass (depth 2)
630+ const shallowResponse = await fetch ( 'http://localhost:13378/graphql' , {
631+ method : 'POST' ,
632+ headers : {
633+ 'Content-Type' : 'application/json' ,
634+ 'X-Parse-Application-Id' : 'test' ,
635+ 'X-Parse-Javascript-Key' : 'test' ,
636+ } ,
637+ body : JSON . stringify ( {
638+ query,
639+ operationName : 'ShallowQuery'
640+ } )
641+ } ) ;
642+ const shallowResult = await shallowResponse . json ( ) ;
643+ expect ( shallowResult . data . users ) . toBeDefined ( ) ;
644+
645+ // DeepQuery should fail (depth 4 > 2 limit)
646+ const deepResponse = await fetch ( 'http://localhost:13378/graphql' , {
647+ method : 'POST' ,
648+ headers : {
649+ 'Content-Type' : 'application/json' ,
650+ 'X-Parse-Application-Id' : 'test' ,
651+ 'X-Parse-Javascript-Key' : 'test' ,
652+ } ,
653+ body : JSON . stringify ( {
654+ query,
655+ operationName : 'DeepQuery'
656+ } )
657+ } ) ;
658+ const deepResult = await deepResponse . json ( ) ;
659+ expect ( deepResult . errors ) . toBeDefined ( ) ;
660+ expect ( deepResult . errors [ 0 ] . message ) . toContain ( 'Query depth exceeds maximum allowed depth' ) ;
661+ } ) ;
662+ } ) ;
532663} ) ;
533664
0 commit comments