-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Thierry DEGREMONT
committed
Jul 19, 2024
1 parent
b2da181
commit bbbc95a
Showing
14 changed files
with
1,202 additions
and
154 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { type Plugin } from '@envelop/core'; | ||
|
||
import {Logger} from '../utils/logger' | ||
|
||
|
||
export default () => { | ||
return <Plugin>{ | ||
onFetch({ context, info }) { | ||
|
||
if (!info) { | ||
return; | ||
} | ||
//rawSource.rawSource.handler.config.source | ||
//info.parentType._fields.getFeatureToggles_v1.description | ||
//info.variableValues | ||
const start = Date.now(); | ||
let rawSource=context[info.sourceName] | ||
console.log("rawSource", rawSource) | ||
let endpoint=rawSource.rawSource.handler.config.endpoint | ||
let operation=info.operation.name | ||
let description=info.parentType._fields[info.path.key].description | ||
return (tt) => { | ||
const duration = Date.now() - start; | ||
let response=tt.response | ||
let status=response.options.status | ||
let url=response.options.url | ||
//const timing = `${info.fieldName};desc="${info.fieldName} (${info.sourceName})";dur=${duration}`; | ||
Logger.onFetch(context.request,info.fieldName,info.sourceName,info.path.key,endpoint,operation,info.variableValues,description,duration) | ||
}; | ||
}, | ||
|
||
onExecute() { | ||
return { | ||
onExecuteDone({ args }) { | ||
// @ts-ignore | ||
const { timings } = args.contextValue; | ||
if (!timings) { | ||
return; | ||
} | ||
|
||
// @ts-ignore | ||
args.contextValue.res.setHeader?.( | ||
'Server-Timing', | ||
timings.join(', ') | ||
); | ||
}, | ||
}; | ||
}, | ||
|
||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { type Plugin } from '@envelop/core' | ||
import { Logger } from '../utils/logger' | ||
import { NoSchemaIntrospectionCustomRule } from 'graphql'; | ||
import { GraphQLError } from 'graphql'; | ||
/** | ||
* monitor plugin in order to get event contextual log and add some security rules | ||
* useful to | ||
* - log the graphql Query event | ||
* - add desabled instropection alidation rule | ||
* - remove suggestion | ||
* - log the execute result summary with executes duration | ||
* - remove not allowed instropection in result | ||
*/ | ||
|
||
const formatter = (error: GraphQLError, mask: string): GraphQLError => { | ||
if (error instanceof GraphQLError) { | ||
error.message = error.message.replace(/Did you mean ".+"/g, mask); | ||
} | ||
return error as GraphQLError; | ||
}; | ||
export default ({ options }): Plugin => { | ||
// not allow by default | ||
// do not enabled enabledIntrospection in production | ||
const enabledIntrospection = process.env['IS_PROUCTION_ENV'] != 'true' && ( options.enabledIntrospection || process.env['ENABLED_INTROSPECTION'] || false ) | ||
// low info in log by default | ||
const resultLogInfoLevel= options.resultLogInfoLevel ? options.resultLogInfoLevel : "low" | ||
|
||
return { | ||
onParse({ params, context }) { | ||
Logger.graphqlQuery(context['request']['headers'], context['params']) | ||
/*return ({ result, context, replaceParseResult }) => { | ||
Logger.info('LOG', 'onParse', 'result', result) | ||
}*/ | ||
}, | ||
|
||
onValidate: ({ addValidationRule, context }) => { | ||
if (!enabledIntrospection) { | ||
addValidationRule(NoSchemaIntrospectionCustomRule) | ||
} | ||
return function onValidateEnd({ valid, result, setResult }) { | ||
if (options.maskSuggestion.enabled && !valid) { | ||
setResult(result.map((error) => formatter(error, options.maskSuggestion.message))); | ||
} | ||
}; | ||
}, | ||
|
||
onExecute({ args, extendContext }) { | ||
|
||
let timestampDebut = new Date().getTime() | ||
return { | ||
before() { | ||
|
||
timestampDebut = new Date().getTime() | ||
}, | ||
onExecuteDone({ result, args }) { | ||
const timestampDone = new Date().getTime(); | ||
|
||
// short cut to desabled introspection response in case of bad configuration rule | ||
if (!enabledIntrospection && args.contextValue['params'].query.includes('__schema')) { | ||
result['data'] = {} | ||
result['errors'] = [{ message: "Fordidden" }] | ||
Logger.error('SECU', 'onExecute', 'Intropection query deteted not allowed', args.contextValue['params']) | ||
} | ||
Logger.endExec(args.contextValue['request']['headers'], result, timestampDone - timestampDebut, resultLogInfoLevel) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { type Plugin } from '@envelop/core'; | ||
|
||
import { Logger } from '../utils/logger' | ||
|
||
/**@ | ||
* monitor fetch source | ||
* use to : | ||
* - add log event for each fetch like, url, response status, duration | ||
*/ | ||
|
||
export default ({ options }) => { | ||
return <Plugin>{ | ||
onFetch({ context, info }) { | ||
|
||
if (!info) { | ||
Logger.warn("noFeychInfo","onFetch","no info in on fetch") | ||
return; | ||
} | ||
const start = Date.now(); | ||
let rawSource = context[info.sourceName] | ||
let description = info.parentType._fields[info.path.key].description | ||
|
||
return (fetch: any) => { | ||
const duration = Date.now() - start; | ||
let fetchInfo = {} | ||
if (options.fullFetchInfo) { | ||
fetchInfo = { | ||
fieldName: info.fieldName, | ||
sourceName: info.sourceName, | ||
pathKey: info.path.key, | ||
operation: info.operation.name, | ||
variables: info.variables, | ||
endpoint: rawSource.rawSource.handler.config.endpoint, | ||
description: description | ||
} | ||
} else { | ||
fetchInfo = { | ||
fieldName: info.fieldName, | ||
pathKey: info.path.key, | ||
operation: info.operation.name, | ||
variables: info.variableValues, | ||
endpoint: rawSource.rawSource.handler.config.endpoint, | ||
} | ||
} | ||
const fetchResponseInfo = {} | ||
if (fetch.response) { | ||
|
||
fetchResponseInfo['status'] = fetch.response.status | ||
fetchResponseInfo['url'] = fetch.response.url | ||
const options = fetch.response.options | ||
if (options) { | ||
fetchResponseInfo['options'] = { | ||
requestId: options.headers['x-request-id'], | ||
server: options.headers['server'] | ||
} | ||
} | ||
} | ||
Logger.onFetch(context.request, fetchInfo, fetchResponseInfo, duration) | ||
}; | ||
}, | ||
|
||
onExecute() { | ||
return { | ||
onExecuteDone({ args }) { | ||
// @ts-ignore | ||
const { timings } = args.contextValue; | ||
if (!timings) { | ||
return; | ||
} | ||
|
||
// @ts-ignore | ||
args.contextValue.res.setHeader?.( | ||
'Server-Timing', | ||
timings.join(', ') | ||
); | ||
}, | ||
}; | ||
}, | ||
|
||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { Plugin } from 'graphql-yoga' | ||
import { Logger } from '../utils/logger' | ||
import { GraphQLError } from 'graphql' | ||
|
||
/** | ||
* monitor plugin in order to get event contextual log and add some security rules | ||
* useful to : | ||
* - log new request comming event | ||
* - add request timestamp in headers to get duration time | ||
* - monitor instropection request | ||
* - mask error in result is required ( ex in production ) | ||
* - log response sumary event | ||
* - remove a eventualy not allowed instropection data in result | ||
*/ | ||
export function useYagaMonitoring({ options }): Plugin { | ||
const isMaskErrors = options.maskError?.enabled || process.env['MASK_ERRORS'] || false | ||
// filter in production anyway | ||
const isFilterError = options.filterError?.enabled || process.env['FILTER_ERRORS'] == 'true' || process.env['IS_PROUCTION_ENV'] == 'true' || false | ||
|
||
const errorMaskMessage = options.maskError?.message ? options.maskError.message : "something goes wrong" | ||
const reponseLogInfoLevel = options.reponseLogInfoLevel ? options.reponseLogInfoLevel : "low" | ||
const resultLogInfoLevel = options.resultLogInfoLevel ? options.resultLogInfoLevel : "low" | ||
|
||
|
||
return { | ||
onRequest({ request/*, fetchAPI, endResponse */ }) { | ||
Logger.onRequest(request) | ||
|
||
// add resuestTimestamp in headers | ||
const timestamp = new Date().getTime(); | ||
request.headers.append("requestTimestamp", String(timestamp)) | ||
}, | ||
onRequestParse(args) { | ||
const beforeTimestamp = new Date().getTime(); | ||
let requestHeaders = args.request.headers | ||
return { | ||
onRequestParseDone(nRequestParseDoneEventPayload) { | ||
const timestamp = new Date().getTime(); | ||
Logger.onRequestParseDone(requestHeaders, nRequestParseDoneEventPayload.requestParserResult['query'], nRequestParseDoneEventPayload.requestParserResult['operationName'], nRequestParseDoneEventPayload.requestParserResult['variables'], timestamp - beforeTimestamp) | ||
if (nRequestParseDoneEventPayload.requestParserResult['query'].includes('__schema')) { | ||
Logger.info("IntrospectionQuery", "onRequestParseDone", "introspection detected", nRequestParseDoneEventPayload.requestParserResult['query']) | ||
} | ||
} | ||
} | ||
}, | ||
onResultProcess(args) { | ||
Logger.onResultProcess(args.request, args.result, resultLogInfoLevel) | ||
if (isMaskErrors) { | ||
if (args.result['errors']) { | ||
let errors = args.result['errors'] | ||
for (let i = 0; i < errors.length; i++) { | ||
errors[i] = errorMaskMessage | ||
} | ||
} | ||
} else { | ||
if (isFilterError) { | ||
if (args.result['errors']) { | ||
let errors = args.result['errors'] | ||
|
||
for (let i = 0; i < errors.length; i++) { | ||
errors[i]= new GraphQLError(filter(errors[i]['message'])) | ||
} | ||
|
||
} | ||
|
||
} | ||
} | ||
}, | ||
|
||
onResponse({ request, response }) { | ||
if (request.method != 'OPTION') { | ||
Logger.onResponse(request, response, reponseLogInfoLevel) | ||
} | ||
} | ||
} | ||
} | ||
|
||
function filter(message: string) { | ||
if (message.includes("introspection has been disabled")) { | ||
return "forbidden" | ||
} | ||
return message | ||
} |
Binary file not shown.
Binary file modified
BIN
-21 Bytes
(99%)
packages/graphql-mesh/local-pkg/inject-additional-transforms-1.0.0.tgz
Binary file not shown.
Oops, something went wrong.