From 77217a8fdb6e5b26d59b1d7283dba719b75d1642 Mon Sep 17 00:00:00 2001 From: Sergey Tatarintsev Date: Mon, 13 Mar 2023 10:57:43 +0100 Subject: [PATCH] fix(client): Fix exponenetial regexp in error formatting code (#17907) In order to render the fancy error message, we try to find a known prisma call in the source code. To do so, we use a reg exp, which turned out to be of exponential complexity. To make matters worth, embedded DMMF in client prior to 4.0.0 could actually trigger exponential backgtracking by itself. Fix #12355 --- .../createErrorMessageWithContext.test.ts | 20 +++++++++++++++++++ .../utils/createErrorMessageWithContext.ts | 11 +++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) 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