From 3e8fb374f390adcb057b5a40a973272c67be8d99 Mon Sep 17 00:00:00 2001 From: Martijn Date: Thu, 30 Jan 2025 09:08:25 +0100 Subject: [PATCH 1/4] feat(harden-plugin): Allow skipping complexity check --- .../src/middleware/query-complexity-plugin.ts | 10 +++++++--- packages/harden-plugin/src/types.ts | 13 +++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/harden-plugin/src/middleware/query-complexity-plugin.ts b/packages/harden-plugin/src/middleware/query-complexity-plugin.ts index f626de7177..872d9688d0 100644 --- a/packages/harden-plugin/src/middleware/query-complexity-plugin.ts +++ b/packages/harden-plugin/src/middleware/query-complexity-plugin.ts @@ -20,11 +20,15 @@ import { HardenPluginOptions } from '../types'; export class QueryComplexityPlugin implements ApolloServerPlugin { constructor(private options: HardenPluginOptions) {} - async requestDidStart({ schema }: GraphQLRequestContext): Promise> { + async requestDidStart(ctx: GraphQLRequestContext): Promise> { const maxQueryComplexity = this.options.maxQueryComplexity ?? 1000; return { didResolveOperation: async ({ request, document }) => { - if (isAdminApi(schema)) { + if (this.options.skip?.(ctx)) { + // Given skip function tells use we should not check this request for complexity + return; + } + if (isAdminApi(ctx.schema)) { // We don't want to apply the cost analysis on the // Admin API, since any expensive operations would require // an authenticated session. @@ -41,7 +45,7 @@ export class QueryComplexityPlugin implements ApolloServerPlugin { ); } const complexity = getComplexity({ - schema, + schema: ctx.schema, query, variables: request.variables, estimators: this.options.queryComplexityEstimators ?? [ diff --git a/packages/harden-plugin/src/types.ts b/packages/harden-plugin/src/types.ts index 096f266422..f235b2d765 100644 --- a/packages/harden-plugin/src/types.ts +++ b/packages/harden-plugin/src/types.ts @@ -1,3 +1,4 @@ +import { GraphQLRequestContext } from '@apollo/server'; import { ComplexityEstimator } from 'graphql-query-complexity'; /** @@ -79,4 +80,16 @@ export interface HardenPluginOptions { * @default 'prod' */ apiMode?: 'dev' | 'prod'; + /** + * @description + * Allows you to skip the complexity check for certain requests. + * + * @example + * ```ts + * HardenPlugin.init({ + * skip: (ctx) => ctx.request.http.headers['x-storefront-ssr-auth'] === 'some-secret-token' + * }), + * ``` + */ + skip?: (ctx: GraphQLRequestContext) => boolean; } From c36943bc75f534a90eb52609802c82570ebc6563 Mon Sep 17 00:00:00 2001 From: Martijn Date: Thu, 30 Jan 2025 11:15:12 +0100 Subject: [PATCH 2/4] feat(harden-plugin): Allow returning a promise from skip function --- .../harden-plugin/src/middleware/query-complexity-plugin.ts | 2 +- packages/harden-plugin/src/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/harden-plugin/src/middleware/query-complexity-plugin.ts b/packages/harden-plugin/src/middleware/query-complexity-plugin.ts index 872d9688d0..eae72bfc80 100644 --- a/packages/harden-plugin/src/middleware/query-complexity-plugin.ts +++ b/packages/harden-plugin/src/middleware/query-complexity-plugin.ts @@ -24,7 +24,7 @@ export class QueryComplexityPlugin implements ApolloServerPlugin { const maxQueryComplexity = this.options.maxQueryComplexity ?? 1000; return { didResolveOperation: async ({ request, document }) => { - if (this.options.skip?.(ctx)) { + if (await this.options.skip?.(ctx)) { // Given skip function tells use we should not check this request for complexity return; } diff --git a/packages/harden-plugin/src/types.ts b/packages/harden-plugin/src/types.ts index f235b2d765..8c7ea04bdd 100644 --- a/packages/harden-plugin/src/types.ts +++ b/packages/harden-plugin/src/types.ts @@ -91,5 +91,5 @@ export interface HardenPluginOptions { * }), * ``` */ - skip?: (ctx: GraphQLRequestContext) => boolean; + skip?: (ctx: GraphQLRequestContext) => Promise | boolean; } From 70478c5836bff8630cd6c962c818ecfbf9cce8e5 Mon Sep 17 00:00:00 2001 From: Martijn Date: Wed, 5 Feb 2025 08:48:54 +0100 Subject: [PATCH 3/4] fix(harden-plugin): Admin API check first for performance --- .../src/middleware/query-complexity-plugin.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/harden-plugin/src/middleware/query-complexity-plugin.ts b/packages/harden-plugin/src/middleware/query-complexity-plugin.ts index eae72bfc80..99dba3dd30 100644 --- a/packages/harden-plugin/src/middleware/query-complexity-plugin.ts +++ b/packages/harden-plugin/src/middleware/query-complexity-plugin.ts @@ -24,16 +24,16 @@ export class QueryComplexityPlugin implements ApolloServerPlugin { const maxQueryComplexity = this.options.maxQueryComplexity ?? 1000; return { didResolveOperation: async ({ request, document }) => { - if (await this.options.skip?.(ctx)) { - // Given skip function tells use we should not check this request for complexity - return; - } if (isAdminApi(ctx.schema)) { // We don't want to apply the cost analysis on the // Admin API, since any expensive operations would require // an authenticated session. return; } + if (await this.options.skip?.(ctx)) { + // Given skip function tells use we should not check this request for complexity + return; + } const query = request.operationName ? separateOperations(document)[request.operationName] : document; From 05197db1dcba34fe3a09fd2aa26c7847d88100c8 Mon Sep 17 00:00:00 2001 From: Martijn Date: Tue, 11 Feb 2025 14:24:54 +0100 Subject: [PATCH 4/4] feat(harden-plugin): Context renaming --- .../src/middleware/query-complexity-plugin.ts | 8 ++++---- packages/harden-plugin/src/types.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/harden-plugin/src/middleware/query-complexity-plugin.ts b/packages/harden-plugin/src/middleware/query-complexity-plugin.ts index 99dba3dd30..2458544798 100644 --- a/packages/harden-plugin/src/middleware/query-complexity-plugin.ts +++ b/packages/harden-plugin/src/middleware/query-complexity-plugin.ts @@ -20,17 +20,17 @@ import { HardenPluginOptions } from '../types'; export class QueryComplexityPlugin implements ApolloServerPlugin { constructor(private options: HardenPluginOptions) {} - async requestDidStart(ctx: GraphQLRequestContext): Promise> { + async requestDidStart(context: GraphQLRequestContext): Promise> { const maxQueryComplexity = this.options.maxQueryComplexity ?? 1000; return { didResolveOperation: async ({ request, document }) => { - if (isAdminApi(ctx.schema)) { + if (isAdminApi(context.schema)) { // We don't want to apply the cost analysis on the // Admin API, since any expensive operations would require // an authenticated session. return; } - if (await this.options.skip?.(ctx)) { + if (await this.options.skip?.(context)) { // Given skip function tells use we should not check this request for complexity return; } @@ -45,7 +45,7 @@ export class QueryComplexityPlugin implements ApolloServerPlugin { ); } const complexity = getComplexity({ - schema: ctx.schema, + schema: context.schema, query, variables: request.variables, estimators: this.options.queryComplexityEstimators ?? [ diff --git a/packages/harden-plugin/src/types.ts b/packages/harden-plugin/src/types.ts index 8c7ea04bdd..2c752133aa 100644 --- a/packages/harden-plugin/src/types.ts +++ b/packages/harden-plugin/src/types.ts @@ -87,9 +87,9 @@ export interface HardenPluginOptions { * @example * ```ts * HardenPlugin.init({ - * skip: (ctx) => ctx.request.http.headers['x-storefront-ssr-auth'] === 'some-secret-token' + * skip: (context) => context.request.http.headers['x-storefront-ssr-auth'] === 'some-secret-token' * }), * ``` */ - skip?: (ctx: GraphQLRequestContext) => Promise | boolean; + skip?: (context: GraphQLRequestContext) => Promise | boolean; }