diff --git a/packages/client/src/runtime/utils/createErrorMessageWithContext.test.ts b/packages/client/src/runtime/utils/createErrorMessageWithContext.test.ts index 0415f6d9dd45..11863425cbc1 100644 --- a/packages/client/src/runtime/utils/createErrorMessageWithContext.test.ts +++ b/packages/client/src/runtime/utils/createErrorMessageWithContext.test.ts @@ -1,4 +1,5 @@ import { fs, vol } from 'memfs' +import { performance } from 'perf_hooks' import { CallSite } from './CallSite' import { createErrorMessageWithContext } from './createErrorMessageWithContext' @@ -424,3 +425,22 @@ test('with windows lines endings', () => { What a terrible failure! `) }) + +test('does not uses polynominal regex', () => { + // from https://devina.io/redos-checker + const example = 'tfindFirs'.repeat(18258) + 's' + + mockFile('/project/some-file.js', example) + + // sync timeouts don't work in jest (https://github.com/facebook/jest/issues/6947), so we have to improvise + const start = performance.now() + + createErrorMessageWithContext({ + originalMethod: 'model.findFirst', + callsite: mockCallsite('/project/some-file.js', 1, 1), + message: 'What a terrible failure!', + }) + const timeTook = performance.now() - start + + expect(timeTook).toBeLessThan(100) +}) diff --git a/packages/client/src/runtime/utils/createErrorMessageWithContext.ts b/packages/client/src/runtime/utils/createErrorMessageWithContext.ts index 6ac190d96861..540f62a0a33e 100644 --- a/packages/client/src/runtime/utils/createErrorMessageWithContext.ts +++ b/packages/client/src/runtime/utils/createErrorMessageWithContext.ts @@ -111,12 +111,17 @@ function getTemplateParameters( function findPrismaActionCall(str: string): { code: string; openingBraceIndex: number } | null { const allActions = Object.keys(DMMF.ModelAction).join('|') - const regexp = new RegExp(String.raw`\S+(${allActions})\(`) + const regexp = new RegExp(String.raw`\.(${allActions})\(`) const match = regexp.exec(str) if (match) { + const openingBraceIndex = match.index + match[0].length + // to get the code we are slicing the string up to a found brace. We start + // with first non-space character if space is found in the line before that or + // 0 if it is not. + const statementStart = str.lastIndexOf(' ', match.index) + 1 return { - code: match[0], - openingBraceIndex: match.index + match[0].length, + code: str.slice(statementStart, openingBraceIndex), + openingBraceIndex, } } return null