diff --git a/src/App.tsx b/src/App.tsx index de66aa8d4b..38657fa3b9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,13 +2,12 @@ import { ThemeProvider } from '@mui/material/styles'; import { LocalizationProvider } from '@mui/x-date-pickers'; import LogRocket from 'logrocket'; import setupLogRocketReact from 'logrocket-react'; -import { ApolloProvider } from './api'; +import { ApolloProvider, GqlSensitiveOperations } from './api'; import { LuxonCalenderDateUtils } from './common/LuxonCalenderDateUtils'; import { ConfettiProvider } from './components/Confetti'; import { Nest } from './components/Nest'; import { SnackbarProvider } from './components/Snackbar'; import { UploadProvider as FileUploadProvider } from './components/Upload'; -import { SensitiveOperations } from './scenes/Authentication'; import { Root } from './scenes/Root'; import { createTheme } from './theme'; @@ -19,7 +18,11 @@ if (logRocketAppId) { network: { requestSanitizer(request) { // Relies on operation name suffix which do in Apollo HttpLink config - if (SensitiveOperations.some((op) => request.url.endsWith('/' + op))) { + if ( + [...GqlSensitiveOperations].some((op) => + request.url.endsWith(`/${op}`) + ) + ) { request.body = undefined; } return request; diff --git a/src/api/operationsList/operations.codegen.ts b/src/api/operationsList/operations.codegen.ts index 5c369ed902..195fe22088 100644 --- a/src/api/operationsList/operations.codegen.ts +++ b/src/api/operationsList/operations.codegen.ts @@ -1,7 +1,49 @@ import { plugin as basePlugin } from '@graphql-codegen/named-operations-object'; import type { PluginFunction } from '@graphql-codegen/plugin-helpers'; +import { concatAST, FieldNode, visit } from 'graphql'; export const plugin: PluginFunction = async (...args) => { - const result = await basePlugin(...args); - return result ? result + ' as const' : result; + let result = await basePlugin(...args); + result = result ? result + ' as const;' : result; + + const [schema, documents] = args; + + const queries = Object.values(schema.getQueryType()?.getFields() ?? {}); + const mutations = Object.values(schema.getMutationType()?.getFields() ?? {}); + const sensitiveQueries = new Set( + [...queries, ...mutations] + .filter( + (f) => f.description && /^\s*@sensitive-secrets$/m.test(f.description) + ) + .map((f) => f.name) + ); + const sensitiveOperationsNames = new Set(); + const allAst = concatAST(documents.flatMap((v) => v.document ?? [])); + visit(allAst, { + OperationDefinition: (node) => { + if ( + !node.name || + (node.operation !== 'query' && node.operation !== 'mutation') + ) { + return; + } + const fields = node.selectionSet.selections + .filter((s): s is FieldNode => s.kind === 'Field') + .map((s) => s.name.value); + for (const field of fields) { + if (sensitiveQueries.has(field)) { + sensitiveOperationsNames.add(node.name.value); + } + } + }, + }); + + result += ` + +export const GqlSensitiveOperations: ReadonlySet = new Set([ +${[...sensitiveOperationsNames].map((n) => ` '${n}'`).join(',\n')} +]); +`; + + return result; }; diff --git a/src/scenes/Authentication/index.ts b/src/scenes/Authentication/index.ts index 7984ee25da..9b68fa7cd1 100644 --- a/src/scenes/Authentication/index.ts +++ b/src/scenes/Authentication/index.ts @@ -1,5 +1,3 @@ -import { GQLOperations } from '../../api'; - export * from './AuthLayout'; export * from './AuthWaiting'; export * from './ChangePassword'; @@ -9,10 +7,3 @@ export * from './Logout'; export * from './Register/Register'; export * from './ResetPassword'; export * from './useAuthRequired'; - -export const SensitiveOperations = [ - GQLOperations.Mutation.Login, - GQLOperations.Mutation.ResetPassword, - GQLOperations.Mutation.ChangePassword, - GQLOperations.Mutation.Register, -];