From ab1c5b20ee7a51a47b6067ebfaa71cdb01d1e6a7 Mon Sep 17 00:00:00 2001 From: Sumit Nihalani <nihalanisumit@gmail.com> Date: Mon, 14 Oct 2019 08:40:53 -0700 Subject: [PATCH] refactor: use re2 instead of RegExp (#4441) --- lib/config/presets.ts | 3 +- lib/config/validation.ts | 32 ++++++------------- lib/datasource/go/index.ts | 3 +- lib/manager/ansible/update.ts | 3 +- lib/manager/bazel/extract.ts | 3 +- lib/manager/bazel/update.ts | 5 +-- lib/manager/buildkite/update.ts | 3 +- lib/manager/bundler/extract.ts | 15 ++++----- .../__snapshots__/validation.spec.ts.snap | 6 +--- test/config/validation.spec.ts | 15 ++++++++- 10 files changed, 45 insertions(+), 43 deletions(-) diff --git a/lib/config/presets.ts b/lib/config/presets.ts index 95933fa9d6..9af6b7b778 100644 --- a/lib/config/presets.ts +++ b/lib/config/presets.ts @@ -7,6 +7,7 @@ import * as npm from '../datasource/npm'; import * as gitlab from '../datasource/gitlab'; import { RenovateConfig } from './common'; import { mergeChildConfig } from './utils'; +import { regEx } from '../util/regex'; const datasources = { github, @@ -126,7 +127,7 @@ export function replaceArgs( if (is.string(obj)) { let returnStr = obj; for (const [arg, argVal] of Object.entries(argMapping)) { - const re = new RegExp(`{{${arg}}}`, 'g'); + const re = regEx(`{{${arg}}}`, 'g'); returnStr = returnStr.replace(re, argVal); } return returnStr; diff --git a/lib/config/validation.ts b/lib/config/validation.ts index 91984b7cfe..8c30c3aea5 100644 --- a/lib/config/validation.ts +++ b/lib/config/validation.ts @@ -1,10 +1,10 @@ import is from '@sindresorhus/is'; -import safe from 'safe-regex'; import { getOptions, RenovateOptions } from './definitions'; import { resolveConfigPresets } from './presets'; import { hasValidSchedule, hasValidTimezone } from '../workers/branch/schedule'; import * as managerValidator from './validation-helpers/managers'; import { RenovateConfig, ValidationMessage } from './common'; +import { regEx } from '../util/regex'; const options = getOptions(); @@ -183,13 +183,7 @@ export async function validateConfig( !(val && val.length === 1 && val[0] === '*') ) { try { - RegExp(val as any); - if (!safe(val as any)) { - errors.push({ - depName: 'Configuration Error', - message: `Unsafe regExp for ${currentPath}: \`${val}\``, - }); - } + regEx(val as any); } catch (e) { errors.push({ depName: 'Configuration Error', @@ -198,21 +192,15 @@ export async function validateConfig( } } if (key === 'fileMatch') { - try { - for (const fileMatch of val) { - RegExp(fileMatch); - if (!safe(fileMatch)) { - errors.push({ - depName: 'Configuration Error', - message: `Unsafe regExp for ${currentPath}: \`${fileMatch}\``, - }); - } + for (const fileMatch of val) { + try { + regEx(fileMatch); + } catch (e) { + errors.push({ + depName: 'Configuration Error', + message: `Invalid regExp for ${currentPath}: \`${fileMatch}\``, + }); } - } catch (e) { - errors.push({ - depName: 'Configuration Error', - message: `Invalid regExp for ${currentPath}: \`${val}\``, - }); } } if ( diff --git a/lib/datasource/go/index.ts b/lib/datasource/go/index.ts index 9bbfde96b5..b555235fce 100644 --- a/lib/datasource/go/index.ts +++ b/lib/datasource/go/index.ts @@ -2,6 +2,7 @@ import { logger } from '../../logger'; import got from '../../util/got'; import * as github from '../github'; import { DigestConfig, PkgReleaseConfig, ReleaseResult } from '../common'; +import { regEx } from '../../util/regex'; interface DataSource { datasource: string; @@ -30,7 +31,7 @@ async function getDatasource(name: string): Promise<DataSource | null> { hostType: 'go', })).body; const sourceMatch = res.match( - new RegExp(`<meta\\s+name="go-source"\\s+content="${name}\\s+([^\\s]+)`) + regEx(`<meta\\s+name="go-source"\\s+content="${name}\\s+([^\\s]+)`) ); if (sourceMatch) { const [, goSourceUrl] = sourceMatch; diff --git a/lib/manager/ansible/update.ts b/lib/manager/ansible/update.ts index a24b532877..8b7b2282f6 100644 --- a/lib/manager/ansible/update.ts +++ b/lib/manager/ansible/update.ts @@ -1,6 +1,7 @@ import { logger } from '../../logger'; import { getNewFrom } from '../dockerfile/update'; import { Upgrade } from '../common'; +import { regEx } from '../../util/regex'; export default function updateDependency( fileContent: string, @@ -11,7 +12,7 @@ export default function updateDependency( logger.debug(`ansible.updateDependency(): ${newFrom}`); const lines = fileContent.split('\n'); const lineToChange = lines[upgrade.managerData.lineNumber]; - const imageLine = new RegExp(/^(\s*image:\s*'?"?)[^\s'"]+('?"?\s*)$/); + const imageLine = regEx(`^(\\s*image:\\s*'?"?)[^\\s'"]+('?"?\\s*)$`); if (!lineToChange.match(imageLine)) { logger.debug('No image line found'); return null; diff --git a/lib/manager/bazel/extract.ts b/lib/manager/bazel/extract.ts index f97ba65b08..ae420a6d53 100644 --- a/lib/manager/bazel/extract.ts +++ b/lib/manager/bazel/extract.ts @@ -3,6 +3,7 @@ import parse from 'github-url-from-git'; import { parse as _parse } from 'url'; import { logger } from '../../logger'; import { PackageDependency, PackageFile } from '../common'; +import { regEx } from '../../util/regex'; interface UrlParsedResult { repo: string; @@ -78,7 +79,7 @@ function parseContent(content: string): string[] { (acc, prefix) => [ ...acc, ...content - .split(new RegExp(prefix + '\\s*\\(', 'g')) + .split(regEx(prefix + '\\s*\\(', 'g')) .slice(1) .map(base => { const ind = findBalancedParenIndex(base); diff --git a/lib/manager/bazel/update.ts b/lib/manager/bazel/update.ts index 3f55a4a21a..7f9f4e55d0 100644 --- a/lib/manager/bazel/update.ts +++ b/lib/manager/bazel/update.ts @@ -2,6 +2,7 @@ import { fromStream } from 'hasha'; import got from '../../util/got'; import { logger } from '../../logger'; import { Upgrade } from '../common'; +import { regEx } from '../../util/regex'; function updateWithNewVersion( content: string, @@ -137,7 +138,7 @@ export async function updateDependency( const hash = await getHashFromUrl(url); newDef = setNewHash(upgrade.managerData.def, hash); newDef = newDef.replace( - new RegExp(`(strip_prefix\\s*=\\s*)"[^"]*"`), + regEx(`(strip_prefix\\s*=\\s*)"[^"]*"`), `$1"${shortRepo}-${upgrade.newDigest}"` ); const match = @@ -151,7 +152,7 @@ export async function updateDependency( if (newDef.endsWith('\n')) { existingRegExStr += '\n'; } - const existingDef = new RegExp(existingRegExStr); + const existingDef = regEx(existingRegExStr); // istanbul ignore if if (!fileContent.match(existingDef)) { logger.info('Cannot match existing string'); diff --git a/lib/manager/buildkite/update.ts b/lib/manager/buildkite/update.ts index 14e7da9691..943cd4e09d 100644 --- a/lib/manager/buildkite/update.ts +++ b/lib/manager/buildkite/update.ts @@ -1,5 +1,6 @@ import { logger } from '../../logger'; import { Upgrade } from '../common'; +import { regEx } from '../../util/regex'; export function updateDependency( currentFileContent: string, @@ -10,7 +11,7 @@ export function updateDependency( logger.debug(`buildkite.updateDependency: ${upgrade.newValue}`); const lines = currentFileContent.split('\n'); const lineToChange = lines[lineIdx]; - const depLine = new RegExp(/^(\s+[^#]+#)[^:]+(:.*)$/); + const depLine = regEx(`^(\\s+[^#]+#)[^:]+(:.*)$`); if (!lineToChange.match(depLine)) { logger.debug('No image line found'); return null; diff --git a/lib/manager/bundler/extract.ts b/lib/manager/bundler/extract.ts index 7d7da5032c..3deb6481f9 100644 --- a/lib/manager/bundler/extract.ts +++ b/lib/manager/bundler/extract.ts @@ -2,6 +2,7 @@ import { logger } from '../../logger'; import { isValid } from '../../versioning/ruby'; import { PackageFile, PackageDependency } from '../common'; import { platform } from '../../platform'; +import { regEx } from '../../util/regex'; export { extractPackageFile }; @@ -22,7 +23,7 @@ async function extractPackageFile( sourceMatch = sourceMatch || line.match( - new RegExp(`^source ${delimiter}([^${delimiter}]+)${delimiter}\\s*$`) + regEx(`^source ${delimiter}([^${delimiter}]+)${delimiter}\\s*$`) ); } if (sourceMatch) { @@ -32,9 +33,7 @@ async function extractPackageFile( for (const delimiter of delimiters) { rubyMatch = rubyMatch || - line.match( - new RegExp(`^ruby ${delimiter}([^${delimiter}]+)${delimiter}`) - ); + line.match(regEx(`^ruby ${delimiter}([^${delimiter}]+)${delimiter}`)); } if (rubyMatch) { res.compatibility = { ruby: rubyMatch[1] }; @@ -43,9 +42,9 @@ async function extractPackageFile( let gemDelimiter: string; for (const delimiter of delimiters) { const gemMatchRegex = `^gem ${delimiter}([^${delimiter}]+)${delimiter}(,\\s+${delimiter}([^${delimiter}]+)${delimiter}){0,2}`; - if (line.match(new RegExp(gemMatchRegex))) { + if (line.match(regEx(gemMatchRegex))) { gemDelimiter = delimiter; - gemMatch = gemMatch || line.match(new RegExp(gemMatchRegex)); + gemMatch = gemMatch || line.match(regEx(gemMatchRegex)); } } if (gemMatch) { @@ -56,7 +55,7 @@ async function extractPackageFile( if (gemMatch[3]) { dep.currentValue = gemMatch[0] .substring(`gem ${gemDelimiter}${dep.depName}${gemDelimiter},`.length) - .replace(new RegExp(gemDelimiter, 'g'), '') + .replace(regEx(gemDelimiter, 'g'), '') .trim(); if (!isValid(dep.currentValue)) { dep.skipReason = 'invalid-value'; @@ -100,7 +99,7 @@ async function extractPackageFile( } for (const delimiter of delimiters) { const sourceBlockMatch = line.match( - new RegExp(`^source\\s+${delimiter}(.*?)${delimiter}\\s+do`) + regEx(`^source\\s+${delimiter}(.*?)${delimiter}\\s+do`) ); if (sourceBlockMatch) { const repositoryUrl = sourceBlockMatch[1]; diff --git a/test/config/__snapshots__/validation.spec.ts.snap b/test/config/__snapshots__/validation.spec.ts.snap index efe8cdf624..72bc3fe2ad 100644 --- a/test/config/__snapshots__/validation.spec.ts.snap +++ b/test/config/__snapshots__/validation.spec.ts.snap @@ -59,10 +59,6 @@ Array [ "depName": "Configuration Error", "message": "Invalid regExp for npm.fileMatch: \`abc ([a-z]+) ([a-z]+))\`", }, - Object { - "depName": "Configuration Error", - "message": "Unsafe regExp for docker.fileMatch: \`(x+x+)+y\`", - }, ] `; @@ -113,7 +109,7 @@ Array [ }, Object { "depName": "Configuration Error", - "message": "Unsafe regExp for packageRules[0].excludePackagePatterns: \`(x+x+)+y\`", + "message": "Invalid regExp for packageRules[0].excludePackagePatterns: \`(x+x+)+y\`", }, ] `; diff --git a/test/config/validation.spec.ts b/test/config/validation.spec.ts index 38f4ad7be9..a0d3f3b944 100644 --- a/test/config/validation.spec.ts +++ b/test/config/validation.spec.ts @@ -138,6 +138,7 @@ describe('config/validation', () => { expect(errors).toMatchSnapshot(); expect(errors).toHaveLength(0); }); + it('errors for unsafe fileMatches', async () => { const config = { npm: { @@ -151,8 +152,20 @@ describe('config/validation', () => { config ); expect(warnings).toHaveLength(0); - expect(errors).toHaveLength(2); + expect(errors).toHaveLength(1); expect(errors).toMatchSnapshot(); }); + + it('validates regEx for each fileMatch', async () => { + const config = { + fileMatch: ['js', '***$}{]]['], + }; + const { warnings, errors } = await configValidation.validateConfig( + config, + true + ); + expect(warnings).toHaveLength(0); + expect(errors).toHaveLength(1); + }); }); }); -- GitLab