From af91be7ea4561ac8f1b8983713c89fe64a8a96bf Mon Sep 17 00:00:00 2001 From: envsecure Date: Sat, 2 May 2026 13:06:01 +0530 Subject: [PATCH 1/2] feat: add default GraphQL query depth limiting This PR implements query depth limiting to protect the Keystone server from deeply nested circular queries that can cause resource exhaustion (DoS). - Added 'maxDepth' option to KeystoneConfig (default: 20). - Integrated 'graphql-depth-limit' validation rule into Apollo Server setup. - Added 'graphql-depth-limit' as a dependency to @keystone-6/core. Resolves #9789 --- packages/core/package.json | 1 + packages/core/src/lib/express.ts | 6 ++++++ packages/core/src/types/config/index.ts | 7 +++++++ 3 files changed, 14 insertions(+) diff --git a/packages/core/package.json b/packages/core/package.json index 4ce53cf639d..629cdba2048 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", diff --git a/packages/core/src/lib/express.ts b/packages/core/src/lib/express.ts index 3ef65128d16..df427e54cd4 100644 --- a/packages/core/src/lib/express.ts +++ b/packages/core/src/lib/express.ts @@ -8,6 +8,8 @@ import { type ApolloServerOptions, ApolloServer } from '@apollo/server' import { ApolloServerPluginLandingPageDisabled } from '@apollo/server/plugin/disabled' import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default' // @ts-expect-error +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 From 4f019c14523a178af577ab8455e52e6eee44f27a Mon Sep 17 00:00:00 2001 From: envsecure Date: Wed, 6 May 2026 13:35:25 +0530 Subject: [PATCH 2/2] chore: add types and remove ts-expect-error for depth limit --- packages/core/package.json | 3 ++- packages/core/src/lib/express.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 629cdba2048..fcc1c9d4482 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -300,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 df427e54cd4..8962c82f480 100644 --- a/packages/core/src/lib/express.ts +++ b/packages/core/src/lib/express.ts @@ -7,7 +7,7 @@ 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' -// @ts-expect-error + import depthLimit from 'graphql-depth-limit' // @ts-expect-error import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.js'