Skip to content

Commit

Permalink
fix(client): Fix exponenetial regexp in error formatting code (#17907)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
SevInf committed Mar 13, 2023
1 parent 98eb6ed commit 77217a8
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 3 deletions.
@@ -1,4 +1,5 @@
import { fs, vol } from 'memfs'
import { performance } from 'perf_hooks'

import { CallSite } from './CallSite'
import { createErrorMessageWithContext } from './createErrorMessageWithContext'
Expand Down Expand Up @@ -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)
})
Expand Up @@ -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
Expand Down

0 comments on commit 77217a8

Please sign in to comment.