fix: Data schema exposed via GraphQL API public introspection (GHSA-48q3-prgv-gm4w) (#9819)
This commit is contained in:
@@ -4,7 +4,7 @@ import { ApolloServer } from '@apollo/server';
|
||||
import { expressMiddleware } from '@apollo/server/express4';
|
||||
import { ApolloServerPluginCacheControlDisabled } from '@apollo/server/plugin/disabled';
|
||||
import express from 'express';
|
||||
import { execute, subscribe } from 'graphql';
|
||||
import { execute, subscribe, GraphQLError } from 'graphql';
|
||||
import { SubscriptionServer } from 'subscriptions-transport-ws';
|
||||
import { handleParseErrors, handleParseHeaders, handleParseSession } from '../middlewares';
|
||||
import requiredParameter from '../requiredParameter';
|
||||
@@ -12,6 +12,45 @@ import defaultLogger from '../logger';
|
||||
import { ParseGraphQLSchema } from './ParseGraphQLSchema';
|
||||
import ParseGraphQLController, { ParseGraphQLConfig } from '../Controllers/ParseGraphQLController';
|
||||
|
||||
|
||||
const IntrospectionControlPlugin = (publicIntrospection) => ({
|
||||
|
||||
|
||||
requestDidStart: (requestContext) => ({
|
||||
|
||||
didResolveOperation: async () => {
|
||||
// If public introspection is enabled, we allow all introspection queries
|
||||
if (publicIntrospection) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isMasterOrMaintenance = requestContext.contextValue.auth?.isMaster || requestContext.contextValue.auth?.isMaintenance
|
||||
if (isMasterOrMaintenance) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Now we check if the query is an introspection query
|
||||
// this check strategy should work in 99.99% cases
|
||||
// we can have an issue if a user name a field or class __schemaSomething
|
||||
// we want to avoid a full AST check
|
||||
const isIntrospectionQuery =
|
||||
requestContext.request.query?.includes('__schema')
|
||||
|
||||
if (isIntrospectionQuery) {
|
||||
throw new GraphQLError('Introspection is not allowed', {
|
||||
extensions: {
|
||||
http: {
|
||||
status: 403,
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
class ParseGraphQLServer {
|
||||
parseGraphQLController: ParseGraphQLController;
|
||||
|
||||
@@ -65,8 +104,8 @@ class ParseGraphQLServer {
|
||||
// needed since we use graphql upload
|
||||
requestHeaders: ['X-Parse-Application-Id'],
|
||||
},
|
||||
introspection: true,
|
||||
plugins: [ApolloServerPluginCacheControlDisabled()],
|
||||
introspection: this.config.graphQLPublicIntrospection,
|
||||
plugins: [ApolloServerPluginCacheControlDisabled(), IntrospectionControlPlugin(this.config.graphQLPublicIntrospection)],
|
||||
schema,
|
||||
});
|
||||
await apollo.start();
|
||||
@@ -118,7 +157,7 @@ class ParseGraphQLServer {
|
||||
|
||||
app.get(
|
||||
this.config.playgroundPath ||
|
||||
requiredParameter('You must provide a config.playgroundPath to applyPlayground!'),
|
||||
requiredParameter('You must provide a config.playgroundPath to applyPlayground!'),
|
||||
(_req, res) => {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.write(
|
||||
|
||||
Reference in New Issue
Block a user