@@ -808,6 +808,71 @@ describe('ParseGraphQLServer', () => {
808808 }
809809 } ) ;
810810
811+ it ( 'should block __type introspection through nested fragment spreads without master key' , async ( ) => {
812+ try {
813+ await apolloClient . query ( {
814+ query : gql `
815+ fragment InnerFragment on Query {
816+ __type(name: "User") {
817+ name
818+ fields {
819+ name
820+ }
821+ }
822+ }
823+
824+ fragment OuterFragment on Query {
825+ ...InnerFragment
826+ }
827+
828+ query NestedFragmentIntrospection {
829+ ...OuterFragment
830+ }
831+ ` ,
832+ } ) ;
833+
834+ fail ( 'should have thrown an error' ) ;
835+ } catch ( e ) {
836+ expect ( e . message ) . toEqual ( 'Response not successful: Received status code 403' ) ;
837+ expect ( e . networkError . result . errors [ 0 ] . message ) . toEqual ( 'Introspection is not allowed' ) ;
838+ }
839+ } ) ;
840+
841+ it ( 'should block __type introspection hidden in fragment with valid field without master key' , async ( ) => {
842+ try {
843+ // First create a test object to query
844+ const object = new Parse . Object ( 'SomeClass' ) ;
845+ await object . save ( ) ;
846+
847+ await apolloClient . query ( {
848+ query : gql `
849+ fragment MixedFragment on Query {
850+ someClasses {
851+ edges {
852+ node {
853+ objectId
854+ }
855+ }
856+ }
857+ __type(name: "User") {
858+ name
859+ kind
860+ }
861+ }
862+
863+ query MixedQuery {
864+ ...MixedFragment
865+ }
866+ ` ,
867+ } ) ;
868+
869+ fail ( 'should have thrown an error' ) ;
870+ } catch ( e ) {
871+ expect ( e . message ) . toEqual ( 'Response not successful: Received status code 403' ) ;
872+ expect ( e . networkError . result . errors [ 0 ] . message ) . toEqual ( 'Introspection is not allowed' ) ;
873+ }
874+ } ) ;
875+
811876 it ( 'should allow __type introspection with master key' , async ( ) => {
812877 const introspection = await apolloClient . query ( {
813878 query : gql `
@@ -6865,7 +6930,7 @@ describe('ParseGraphQLServer', () => {
68656930 ) ;
68666931 expect (
68676932 ( await deleteObject ( object4 . className , object4 . id ) ) . data . delete [
6868- object4 . className . charAt ( 0 ) . toLowerCase ( ) + object4 . className . slice ( 1 )
6933+ object4 . className . charAt ( 0 ) . toLowerCase ( ) + object4 . className . slice ( 1 )
68696934 ]
68706935 ) . toEqual ( { objectId : object4 . id , __typename : 'PublicClass' } ) ;
68716936 await expectAsync ( object4 . fetch ( { useMasterKey : true } ) ) . toBeRejectedWith (
@@ -11472,25 +11537,25 @@ describe('ParseGraphQLServer', () => {
1147211537 } ,
1147311538 } ) ;
1147411539 const SomeClassType = new GraphQLObjectType ( {
11475- name : 'SomeClass' ,
11476- fields : {
11477- nameUpperCase : {
11478- type : new GraphQLNonNull ( GraphQLString ) ,
11479- resolve : p => p . name . toUpperCase ( ) ,
11480- } ,
11481- type : { type : TypeEnum } ,
11482- language : {
11483- type : new GraphQLEnumType ( {
11484- name : 'LanguageEnum' ,
11485- values : {
11486- fr : { value : 'fr' } ,
11487- en : { value : 'en' } ,
11488- } ,
11489- } ) ,
11490- resolve : ( ) => 'fr' ,
11491- } ,
11540+ name : 'SomeClass' ,
11541+ fields : {
11542+ nameUpperCase : {
11543+ type : new GraphQLNonNull ( GraphQLString ) ,
11544+ resolve : p => p . name . toUpperCase ( ) ,
1149211545 } ,
11493- } ) ,
11546+ type : { type : TypeEnum } ,
11547+ language : {
11548+ type : new GraphQLEnumType ( {
11549+ name : 'LanguageEnum' ,
11550+ values : {
11551+ fr : { value : 'fr' } ,
11552+ en : { value : 'en' } ,
11553+ } ,
11554+ } ) ,
11555+ resolve : ( ) => 'fr' ,
11556+ } ,
11557+ } ,
11558+ } ) ,
1149411559 parseGraphQLServer = new ParseGraphQLServer ( parseServer , {
1149511560 graphQLPath : '/graphql' ,
1149611561 graphQLCustomTypeDefs : new GraphQLSchema ( {
0 commit comments