diff --git a/packages/core/package.json b/packages/core/package.json index 4ce53cf639d..fcc1c9d4482 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -273,6 +273,7 @@ "fast-deep-equal": "^3.1.3", "fs-extra": "^11.0.0", "graphql": "^16.8.1", + "graphql-depth-limit": "^1.1.0", "graphql-upload": "^15.0.2", "image-size": "^2.0.0", "intersection-observer": "^0.12.0", @@ -299,7 +300,8 @@ "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@types/resolve": "^1.20.2", - "@types/uuid": "^11.0.0" + "@types/uuid": "^11.0.0", + "@types/graphql-depth-limit": "^1.1.6" }, "preconstruct": { "entrypoints": [ diff --git a/packages/core/src/lib/express.ts b/packages/core/src/lib/express.ts index 3ef65128d16..8962c82f480 100644 --- a/packages/core/src/lib/express.ts +++ b/packages/core/src/lib/express.ts @@ -7,6 +7,8 @@ import { GraphQLError, type GraphQLFormattedError } from 'graphql' import { type ApolloServerOptions, ApolloServer } from '@apollo/server' import { ApolloServerPluginLandingPageDisabled } from '@apollo/server/plugin/disabled' import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default' + +import depthLimit from 'graphql-depth-limit' // @ts-expect-error import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.js' import type { KeystoneContext, KeystoneConfig } from '../types' @@ -74,6 +76,10 @@ export async function createExpressServer( ...apolloConfig, formatError: formatError(config.graphql), schema: context.graphql.schema, + validationRules: [ + depthLimit(config.graphql.maxDepth ?? 20), + ...(apolloConfig?.validationRules ?? []), + ], plugins: config.graphql.playground === 'apollo' ? apolloConfig?.plugins diff --git a/packages/core/src/types/config/index.ts b/packages/core/src/types/config/index.ts index 4f831ad4c66..acf1bbeab1e 100644 --- a/packages/core/src/types/config/index.ts +++ b/packages/core/src/types/config/index.ts @@ -116,9 +116,16 @@ export type KeystoneConfigPre GraphQLSchema + + /** + * The maximum depth allowed for queries. + * @default 20 + */ + maxDepth?: number } lists: Record> + server?: { /** Configuration options for the cors middleware. Set to `true` to use Keystone's defaults */ cors?: boolean | CorsOptions