@@ -4645,4 +4645,123 @@ describe('(GHSA-wp76-gg32-8258) /verifyPassword leaks raw authData via missing a
46454645 expect ( response . data . authData ?. mfa ?. recovery ) . toBeUndefined ( ) ;
46464646 expect ( response . data . authData ?. mfa ) . toEqual ( { status : 'enabled' } ) ;
46474647 } ) ;
4648+
4649+ describe ( '(GHSA-q3p6-g7c4-829c) GraphQL endpoint ignores allowOrigin server option' , ( ) => {
4650+ let httpServer ;
4651+ const gqlPort = 13398 ;
4652+
4653+ const gqlHeaders = {
4654+ 'X-Parse-Application-Id' : 'test' ,
4655+ 'X-Parse-Javascript-Key' : 'test' ,
4656+ 'Content-Type' : 'application/json' ,
4657+ } ;
4658+
4659+ async function setupGraphQLServer ( serverOptions = { } ) {
4660+ if ( httpServer ) {
4661+ await new Promise ( resolve => httpServer . close ( resolve ) ) ;
4662+ }
4663+ const server = await reconfigureServer ( serverOptions ) ;
4664+ const expressApp = express ( ) ;
4665+ httpServer = http . createServer ( expressApp ) ;
4666+ expressApp . use ( '/parse' , server . app ) ;
4667+ const parseGraphQLServer = new ParseGraphQLServer ( server , {
4668+ graphQLPath : '/graphql' ,
4669+ } ) ;
4670+ parseGraphQLServer . applyGraphQL ( expressApp ) ;
4671+ await new Promise ( resolve => httpServer . listen ( { port : gqlPort } , resolve ) ) ;
4672+ return parseGraphQLServer ;
4673+ }
4674+
4675+ afterEach ( async ( ) => {
4676+ if ( httpServer ) {
4677+ await new Promise ( resolve => httpServer . close ( resolve ) ) ;
4678+ httpServer = null ;
4679+ }
4680+ } ) ;
4681+
4682+ it ( 'should reflect allowed origin when allowOrigin is configured' , async ( ) => {
4683+ await setupGraphQLServer ( { allowOrigin : 'https://example.com' } ) ;
4684+ const response = await fetch ( `http://localhost:${ gqlPort } /graphql` , {
4685+ method : 'POST' ,
4686+ headers : { ...gqlHeaders , Origin : 'https://example.com' } ,
4687+ body : JSON . stringify ( { query : '{ health }' } ) ,
4688+ } ) ;
4689+ expect ( response . status ) . toBe ( 200 ) ;
4690+ expect ( response . headers . get ( 'access-control-allow-origin' ) ) . toBe ( 'https://example.com' ) ;
4691+ } ) ;
4692+
4693+ it ( 'should not reflect unauthorized origin when allowOrigin is configured' , async ( ) => {
4694+ await setupGraphQLServer ( { allowOrigin : 'https://example.com' } ) ;
4695+ const response = await fetch ( `http://localhost:${ gqlPort } /graphql` , {
4696+ method : 'POST' ,
4697+ headers : { ...gqlHeaders , Origin : 'https://unauthorized.example.net' } ,
4698+ body : JSON . stringify ( { query : '{ health }' } ) ,
4699+ } ) ;
4700+ expect ( response . headers . get ( 'access-control-allow-origin' ) ) . not . toBe ( 'https://unauthorized.example.net' ) ;
4701+ expect ( response . headers . get ( 'access-control-allow-origin' ) ) . toBe ( 'https://example.com' ) ;
4702+ } ) ;
4703+
4704+ it ( 'should support multiple allowed origins' , async ( ) => {
4705+ await setupGraphQLServer ( { allowOrigin : [ 'https://a.example.com' , 'https://b.example.com' ] } ) ;
4706+ const responseA = await fetch ( `http://localhost:${ gqlPort } /graphql` , {
4707+ method : 'POST' ,
4708+ headers : { ...gqlHeaders , Origin : 'https://a.example.com' } ,
4709+ body : JSON . stringify ( { query : '{ health }' } ) ,
4710+ } ) ;
4711+ expect ( responseA . headers . get ( 'access-control-allow-origin' ) ) . toBe ( 'https://a.example.com' ) ;
4712+
4713+ const responseB = await fetch ( `http://localhost:${ gqlPort } /graphql` , {
4714+ method : 'POST' ,
4715+ headers : { ...gqlHeaders , Origin : 'https://b.example.com' } ,
4716+ body : JSON . stringify ( { query : '{ health }' } ) ,
4717+ } ) ;
4718+ expect ( responseB . headers . get ( 'access-control-allow-origin' ) ) . toBe ( 'https://b.example.com' ) ;
4719+
4720+ const responseUnauthorized = await fetch ( `http://localhost:${ gqlPort } /graphql` , {
4721+ method : 'POST' ,
4722+ headers : { ...gqlHeaders , Origin : 'https://unauthorized.example.net' } ,
4723+ body : JSON . stringify ( { query : '{ health }' } ) ,
4724+ } ) ;
4725+ expect ( responseUnauthorized . headers . get ( 'access-control-allow-origin' ) ) . not . toBe ( 'https://unauthorized.example.net' ) ;
4726+ expect ( responseUnauthorized . headers . get ( 'access-control-allow-origin' ) ) . toBe ( 'https://a.example.com' ) ;
4727+ } ) ;
4728+
4729+ it ( 'should default to wildcard when allowOrigin is not configured' , async ( ) => {
4730+ await setupGraphQLServer ( ) ;
4731+ const response = await fetch ( `http://localhost:${ gqlPort } /graphql` , {
4732+ method : 'POST' ,
4733+ headers : { ...gqlHeaders , Origin : 'https://example.com' } ,
4734+ body : JSON . stringify ( { query : '{ health }' } ) ,
4735+ } ) ;
4736+ expect ( response . headers . get ( 'access-control-allow-origin' ) ) . toBe ( '*' ) ;
4737+ } ) ;
4738+
4739+ it ( 'should handle OPTIONS preflight with configured allowOrigin' , async ( ) => {
4740+ await setupGraphQLServer ( { allowOrigin : 'https://example.com' } ) ;
4741+ const response = await fetch ( `http://localhost:${ gqlPort } /graphql` , {
4742+ method : 'OPTIONS' ,
4743+ headers : {
4744+ Origin : 'https://example.com' ,
4745+ 'Access-Control-Request-Method' : 'POST' ,
4746+ 'Access-Control-Request-Headers' : 'X-Parse-Application-Id, Content-Type' ,
4747+ } ,
4748+ } ) ;
4749+ expect ( response . status ) . toBe ( 200 ) ;
4750+ expect ( response . headers . get ( 'access-control-allow-origin' ) ) . toBe ( 'https://example.com' ) ;
4751+ } ) ;
4752+
4753+ it ( 'should not reflect unauthorized origin in OPTIONS preflight' , async ( ) => {
4754+ await setupGraphQLServer ( { allowOrigin : 'https://example.com' } ) ;
4755+ const response = await fetch ( `http://localhost:${ gqlPort } /graphql` , {
4756+ method : 'OPTIONS' ,
4757+ headers : {
4758+ Origin : 'https://unauthorized.example.net' ,
4759+ 'Access-Control-Request-Method' : 'POST' ,
4760+ 'Access-Control-Request-Headers' : 'X-Parse-Application-Id, Content-Type' ,
4761+ } ,
4762+ } ) ;
4763+ expect ( response . headers . get ( 'access-control-allow-origin' ) ) . not . toBe ( 'https://unauthorized.example.net' ) ;
4764+ expect ( response . headers . get ( 'access-control-allow-origin' ) ) . toBe ( 'https://example.com' ) ;
4765+ } ) ;
4766+ } ) ;
46484767} ) ;
0 commit comments