From 869ec980458df3b22dcc2ed128cedc9d3a85c54b Mon Sep 17 00:00:00 2001 From: Giancarlo Anemone Date: Mon, 13 Nov 2023 19:20:49 -0500 Subject: [PATCH] Add `dangerouslyDisableValidation` option to `@apollo/server` (#7786) This adds a `dangerouslyDisableValidation` option to `@apollo/server` which will skip the validation step for graphql operations. --- .changeset/blue-laws-grab.md | 6 +++ docs/source/api/apollo-server.mdx | 15 +++++++ .../src/apolloServerTests.ts | 15 +++++++ packages/server/src/ApolloServer.ts | 4 +- .../server/src/externalTypes/constructor.ts | 1 + packages/server/src/requestPipeline.ts | 40 ++++++++++--------- 6 files changed, 61 insertions(+), 20 deletions(-) create mode 100644 .changeset/blue-laws-grab.md diff --git a/.changeset/blue-laws-grab.md b/.changeset/blue-laws-grab.md new file mode 100644 index 00000000000..6e261ee15e5 --- /dev/null +++ b/.changeset/blue-laws-grab.md @@ -0,0 +1,6 @@ +--- +'@apollo/server-integration-testsuite': minor +'@apollo/server': minor +--- + +Restore missing v1 `skipValidation` option as `dangerouslyDisableValidation`. Note that enabling this option exposes your server to potential security and unexpected runtime issues. Apollo will not support issues that arise as a result of using this option. diff --git a/docs/source/api/apollo-server.mdx b/docs/source/api/apollo-server.mdx index eed454115b8..e1522cacdb1 100644 --- a/docs/source/api/apollo-server.mdx +++ b/docs/source/api/apollo-server.mdx @@ -492,6 +492,21 @@ Apollo Server v5 will _always_ behave as if this option is `true` (and will igno + + + +##### `dangerouslyDisableValidation` + +`Boolean` + + + +⚠️ Caution: this option can lead to security vulnerabilities and unexpected behavior. Use of this option in production is not supported by Apollo. + +When set to `true`, disable validation of graphql operations entirely. + + + diff --git a/packages/integration-testsuite/src/apolloServerTests.ts b/packages/integration-testsuite/src/apolloServerTests.ts index fabfb446492..102db81bb11 100644 --- a/packages/integration-testsuite/src/apolloServerTests.ts +++ b/packages/integration-testsuite/src/apolloServerTests.ts @@ -312,6 +312,21 @@ export function defineIntegrationTestSuiteApolloServerTests( ); }); + it('allows disabling validation rules', async () => { + const uri = await createServerAndGetUrl({ + schema, + stopOnTerminationSignals: false, + nodeEnv: 'production', + dangerouslyDisableValidation: true, + }); + + const apolloFetch = createApolloFetch({ uri }); + + const result = await apolloFetch({ query: INTROSPECTION_QUERY }); + expect(result.data).toBeDefined(); + expect(result.errors).toBeUndefined(); + }); + it('allows introspection to be enabled explicitly', async () => { const uri = await createServerAndGetUrl({ schema, diff --git a/packages/server/src/ApolloServer.ts b/packages/server/src/ApolloServer.ts index 3ecf63b46fb..9455652e21f 100644 --- a/packages/server/src/ApolloServer.ts +++ b/packages/server/src/ApolloServer.ts @@ -155,7 +155,7 @@ type ServerState = export interface ApolloServerInternals { state: ServerState; gatewayExecutor: GatewayExecutor | null; - + dangerouslyDisableValidation?: boolean; formatError?: ( formattedError: GraphQLFormattedError, error: unknown, @@ -296,6 +296,8 @@ export class ApolloServer { ...(config.validationRules ?? []), ...(introspectionEnabled ? [] : [NoIntrospection]), ], + dangerouslyDisableValidation: + config.dangerouslyDisableValidation ?? false, fieldResolver: config.fieldResolver, includeStacktraceInErrorResponses: config.includeStacktraceInErrorResponses ?? diff --git a/packages/server/src/externalTypes/constructor.ts b/packages/server/src/externalTypes/constructor.ts index ffd1cf6f783..04cf0653d82 100644 --- a/packages/server/src/externalTypes/constructor.ts +++ b/packages/server/src/externalTypes/constructor.ts @@ -97,6 +97,7 @@ interface ApolloServerOptionsBase { apollo?: ApolloConfigInput; nodeEnv?: string; documentStore?: DocumentStore | null; + dangerouslyDisableValidation?: boolean; csrfPrevention?: CSRFPreventionOptions | boolean; // Used for parsing operations; unlike in AS3, this is not also used for diff --git a/packages/server/src/requestPipeline.ts b/packages/server/src/requestPipeline.ts index 33987974088..b798cc181ab 100644 --- a/packages/server/src/requestPipeline.ts +++ b/packages/server/src/requestPipeline.ts @@ -235,27 +235,29 @@ export async function processGraphQLRequest( } await parsingDidEnd(); - const validationDidEnd = await invokeDidStartHook( - requestListeners, - async (l) => - l.validationDidStart?.( - requestContext as GraphQLRequestContextValidationDidStart, - ), - ); - - const validationErrors = validate( - schemaDerivedData.schema, - requestContext.document, - [...specifiedRules, ...internals.validationRules], - ); + if (internals.dangerouslyDisableValidation !== true) { + const validationDidEnd = await invokeDidStartHook( + requestListeners, + async (l) => + l.validationDidStart?.( + requestContext as GraphQLRequestContextValidationDidStart, + ), + ); - if (validationErrors.length === 0) { - await validationDidEnd(); - } else { - await validationDidEnd(validationErrors); - return await sendErrorResponse( - validationErrors.map((error) => new ValidationError(error)), + const validationErrors = validate( + schemaDerivedData.schema, + requestContext.document, + [...specifiedRules, ...internals.validationRules], ); + + if (validationErrors.length === 0) { + await validationDidEnd(); + } else { + await validationDidEnd(validationErrors); + return await sendErrorResponse( + validationErrors.map((error) => new ValidationError(error)), + ); + } } if (schemaDerivedData.documentStore) {