From f62f8dbda29b419ee37f62acdfce1815c1664d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Fri, 19 Jan 2024 15:54:13 +0000 Subject: [PATCH 1/3] fix: enclose regexp when using PCRE --- node/declaration.test.ts | 12 ++++++------ node/declaration.ts | 22 +++++++++++++++++----- node/manifest.ts | 12 ++---------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/node/declaration.test.ts b/node/declaration.test.ts index d67e4332..3d833443 100644 --- a/node/declaration.test.ts +++ b/node/declaration.test.ts @@ -163,23 +163,23 @@ test('netlify.toml-defined excludedPath are respected', () => { test('Does not escape front slashes in a regex pattern if they are already escaped', () => { const regexPattern = '^(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))\\/shows(?:\\/(.*))(.json)?[\\/#\\?]?$' const expected = '^(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))\\/shows(?:\\/(.*))(.json)?[\\/#\\?]?$' - const actual = parsePattern(regexPattern) - expect(actual).toEqual(expected) + expect(parsePattern(regexPattern, false)).toEqual(expected) + expect(parsePattern(regexPattern, true)).toEqual(expected) }) test('Escapes front slashes in a regex pattern', () => { const regexPattern = '^(?:/(_next/data/[^/]{1,}))?(?:/([^/.]{1,}))/shows(?:/(.*))(.json)?[/#\\?]?$' const expected = '^(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))\\/shows(?:\\/(.*))(.json)?[/#\\?]?$' - const actual = parsePattern(regexPattern) - expect(actual).toEqual(expected) + expect(parsePattern(regexPattern, false)).toEqual(expected) + expect(parsePattern(regexPattern, true)).toEqual(expected) }) test('Ensures pattern match on the whole path', () => { const regexPattern = '/foo/.*/bar' const expected = '^\\/foo\\/.*\\/bar$' - const actual = parsePattern(regexPattern) - expect(actual).toEqual(expected) + expect(parsePattern(regexPattern, false)).toEqual(expected) + expect(parsePattern(regexPattern, true)).toEqual(expected) }) diff --git a/node/declaration.ts b/node/declaration.ts index 1b94a733..52cdaa50 100644 --- a/node/declaration.ts +++ b/node/declaration.ts @@ -118,12 +118,25 @@ const createDeclarationsFromFunctionConfigs = ( // Validates and normalizes a pattern so that it's a valid regular expression // in Go, which is the engine used by our edge nodes. -export const parsePattern = (pattern: string) => { +export const parsePattern = (pattern: string, pcreRegexpEngine: boolean) => { let enclosedPattern = pattern - if (!pattern.startsWith('^')) enclosedPattern = `^${enclosedPattern}` - if (!pattern.endsWith('$')) enclosedPattern = `${enclosedPattern}$` + + if (!pattern.startsWith('^')) { + enclosedPattern = `^${enclosedPattern}` + } + + if (!pattern.endsWith('$')) { + enclosedPattern = `${enclosedPattern}$` + } const regexp = new RegExp(enclosedPattern) + const regexpString = pcreRegexpEngine ? transformPCRERegexp(regexp) : regexp.toString() + + // Strip leading and forward slashes. + return regexpString.slice(1, -1) +} + +const transformPCRERegexp = (regexp: RegExp) => { const newRegexp = regexpAST.transform(regexp, { Assertion(path) { // Lookaheads are not supported. If we find one, throw an error. @@ -146,6 +159,5 @@ export const parsePattern = (pattern: string) => { }, }) - // Strip leading and forward slashes. - return newRegexp.toString().slice(1, -1) + return newRegexp.toString() } diff --git a/node/manifest.ts b/node/manifest.ts index c1a968c8..d5213c6e 100644 --- a/node/manifest.ts +++ b/node/manifest.ts @@ -228,12 +228,8 @@ const pathToRegularExpression = (path: string) => { const getRegularExpression = (declaration: Declaration, pcreRegexpEngine: boolean) => { if ('pattern' in declaration) { - if (pcreRegexpEngine) { - return declaration.pattern - } - try { - return parsePattern(declaration.pattern) + return parsePattern(declaration.pattern, pcreRegexpEngine) } catch (error: unknown) { throw wrapBundleError( new Error( @@ -252,13 +248,9 @@ const getExcludedRegularExpressions = (declaration: Declaration, pcreRegexpEngin ? declaration.excludedPattern : [declaration.excludedPattern] - if (pcreRegexpEngine) { - return excludedPatterns - } - return excludedPatterns.map((excludedPattern) => { try { - return parsePattern(excludedPattern) + return parsePattern(excludedPattern, pcreRegexpEngine) } catch (error: unknown) { throw wrapBundleError( new Error( From 4129d2822a6e2c494a1672c616901e482d618d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Fri, 19 Jan 2024 15:56:13 +0000 Subject: [PATCH 2/3] chore: adjust comments --- node/declaration.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/node/declaration.ts b/node/declaration.ts index 52cdaa50..0250a0c7 100644 --- a/node/declaration.ts +++ b/node/declaration.ts @@ -116,8 +116,11 @@ const createDeclarationsFromFunctionConfigs = ( return declarations } -// Validates and normalizes a pattern so that it's a valid regular expression -// in Go, which is the engine used by our edge nodes. +/** + * Normalizes a regular expression, ensuring it has a leading `^` and trailing + * `$` characters. It also converts the regular expression from PCRE to RE2 if + * needed. + */ export const parsePattern = (pattern: string, pcreRegexpEngine: boolean) => { let enclosedPattern = pattern @@ -136,6 +139,10 @@ export const parsePattern = (pattern: string, pcreRegexpEngine: boolean) => { return regexpString.slice(1, -1) } +/** + * Transforms a PCRE regular expression into a RE2 expression, compatible + * with the Go engine used in our edge nodes. + */ const transformPCRERegexp = (regexp: RegExp) => { const newRegexp = regexpAST.transform(regexp, { Assertion(path) { From 1f6d40f828501c12a7be9578d4cd1fc5b836cce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Fri, 19 Jan 2024 15:59:57 +0000 Subject: [PATCH 3/3] fix: doh --- node/declaration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/declaration.ts b/node/declaration.ts index 0250a0c7..9addefde 100644 --- a/node/declaration.ts +++ b/node/declaration.ts @@ -133,7 +133,7 @@ export const parsePattern = (pattern: string, pcreRegexpEngine: boolean) => { } const regexp = new RegExp(enclosedPattern) - const regexpString = pcreRegexpEngine ? transformPCRERegexp(regexp) : regexp.toString() + const regexpString = pcreRegexpEngine ? regexp.toString() : transformPCRERegexp(regexp) // Strip leading and forward slashes. return regexpString.slice(1, -1)