@@ -4141,4 +4141,68 @@ describe('(GHSA-6qh5-m6g3-xhq6) LiveQuery query depth DoS via deeply nested subs
41414141 const subscription = await query . subscribe ( ) ;
41424142 expect ( subscription ) . toBeDefined ( ) ;
41434143 } ) ;
4144+
4145+ describe ( '(GHSA-g4cf-xj29-wqqr) DoS via unindexed database query for unconfigured auth providers' , ( ) => {
4146+ it ( 'should not query database for unconfigured auth provider on signup' , async ( ) => {
4147+ const databaseAdapter = Config . get ( Parse . applicationId ) . database . adapter ;
4148+ const spy = spyOn ( databaseAdapter , 'find' ) . and . callThrough ( ) ;
4149+ await expectAsync (
4150+ new Parse . User ( ) . save ( { authData : { nonExistentProvider : { id : 'test123' } } } )
4151+ ) . toBeRejectedWith (
4152+ new Parse . Error ( Parse . Error . UNSUPPORTED_SERVICE , 'This authentication method is unsupported.' )
4153+ ) ;
4154+ const authDataQueries = spy . calls . all ( ) . filter ( call => {
4155+ const query = call . args [ 2 ] ;
4156+ return query ?. $or ?. some ( q => q [ 'authData.nonExistentProvider.id' ] ) ;
4157+ } ) ;
4158+ expect ( authDataQueries . length ) . toBe ( 0 ) ;
4159+ } ) ;
4160+
4161+ it ( 'should not query database for unconfigured auth provider on challenge' , async ( ) => {
4162+ const databaseAdapter = Config . get ( Parse . applicationId ) . database . adapter ;
4163+ const spy = spyOn ( databaseAdapter , 'find' ) . and . callThrough ( ) ;
4164+ await expectAsync (
4165+ request ( {
4166+ method : 'POST' ,
4167+ url : Parse . serverURL + '/challenge' ,
4168+ headers : {
4169+ 'X-Parse-Application-Id' : Parse . applicationId ,
4170+ 'X-Parse-REST-API-Key' : 'rest' ,
4171+ 'Content-Type' : 'application/json' ,
4172+ } ,
4173+ body : JSON . stringify ( {
4174+ authData : { nonExistentProvider : { id : 'test123' } } ,
4175+ challengeData : { nonExistentProvider : { token : 'abc' } } ,
4176+ } ) ,
4177+ } )
4178+ ) . toBeRejected ( ) ;
4179+ const authDataQueries = spy . calls . all ( ) . filter ( call => {
4180+ const query = call . args [ 2 ] ;
4181+ return query ?. $or ?. some ( q => q [ 'authData.nonExistentProvider.id' ] ) ;
4182+ } ) ;
4183+ expect ( authDataQueries . length ) . toBe ( 0 ) ;
4184+ } ) ;
4185+
4186+ it ( 'should still query database for configured auth provider' , async ( ) => {
4187+ await reconfigureServer ( {
4188+ auth : {
4189+ myConfiguredProvider : {
4190+ module : {
4191+ validateAppId : ( ) => Promise . resolve ( ) ,
4192+ validateAuthData : ( ) => Promise . resolve ( ) ,
4193+ } ,
4194+ } ,
4195+ } ,
4196+ } ) ;
4197+ const databaseAdapter = Config . get ( Parse . applicationId ) . database . adapter ;
4198+ const spy = spyOn ( databaseAdapter , 'find' ) . and . callThrough ( ) ;
4199+ const user = new Parse . User ( ) ;
4200+ await user . save ( { authData : { myConfiguredProvider : { id : 'validId' , token : 'validToken' } } } ) ;
4201+ const authDataQueries = spy . calls . all ( ) . filter ( call => {
4202+ const query = call . args [ 2 ] ;
4203+ return query ?. $or ?. some ( q => q [ 'authData.myConfiguredProvider.id' ] ) ;
4204+ } ) ;
4205+ expect ( authDataQueries . length ) . toBeGreaterThan ( 0 ) ;
4206+ } ) ;
4207+ } ) ;
41444208} ) ;
0 commit comments