diff --git a/lib/config/presets.ts b/lib/config/presets.ts index 95933fa9d61af3118bc99bef472769cbe2c02153..9af6b7b778cc91f58f22f7073e3aa65f143c52a9 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 91984b7cfe38ce22c799d8aaf390440d512466a2..c95f3a09f55fd11231eadd76a2d09fa874149a52 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(); @@ -178,41 +178,30 @@ export async function validateConfig( } } } - if ( - (key === 'packagePatterns' || key === 'excludePackagePatterns') && - !(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}\``, - }); - } - } catch (e) { - errors.push({ - depName: 'Configuration Error', - message: `Invalid regExp for ${currentPath}: \`${val}\``, - }); - } - } - if (key === 'fileMatch') { - try { - for (const fileMatch of val) { - RegExp(fileMatch); - if (!safe(fileMatch)) { + if (key === 'packagePatterns' || key === 'excludePackagePatterns') { + for (const pattern of val) { + if (pattern !== '*') { + try { + regEx(pattern); + } catch (e) { errors.push({ depName: 'Configuration Error', - message: `Unsafe regExp for ${currentPath}: \`${fileMatch}\``, + message: `Invalid regExp for ${currentPath}: \`${pattern}\``, }); } } - } catch (e) { - errors.push({ - depName: 'Configuration Error', - message: `Invalid regExp for ${currentPath}: \`${val}\``, - }); + } + } + if (key === 'fileMatch') { + for (const fileMatch of val) { + try { + regEx(fileMatch); + } catch (e) { + errors.push({ + depName: 'Configuration Error', + message: `Invalid regExp for ${currentPath}: \`${fileMatch}\``, + }); + } } } if ( diff --git a/lib/datasource/go/index.ts b/lib/datasource/go/index.ts index 9bbfde96b5070cb7c10b8103c323727184236696..b555235fce897cf6bfab4d2361fe648059361715 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 a24b5328775000c21b9e1fd20d4f021969600cf0..8b7b2282f6af1ea85302019157fc1439b4a65476 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 f97ba65b08f2f21caaff569e491c2cc0b0bec58c..ae420a6d53515b2066eb51981801fa9cdb5a4136 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 3f55a4a21ab61da0cbc5aa387654dce34855356e..7f9f4e55d0f68948c710a054059dd7b932c31416 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 14e7da9691c531b5e61e54b17ca45f62b9a44dbd..943cd4e09d325633fc5528c3ffbae1d81c39c7af 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 7d7da5032cff0f6cf02579e9efb997ba7e2abc4d..3deb6481f926c230f6743ef161a349e7032d6ce0 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/package.json b/package.json index 1b188500138a9a0fcdf5fbce7f426611f73e8374..e616755b4fcd76e799253f64d2b1a6719318a59c 100644 --- a/package.json +++ b/package.json @@ -142,7 +142,6 @@ "parse-link-header": "1.0.1", "pnpm": "3.8.1", "registry-auth-token": "4.0.0", - "safe-regex": "2.0.2", "semver": "6.3.0", "semver-stable": "3.0.0", "semver-utils": "1.1.4", diff --git a/test/config/__snapshots__/validation.spec.ts.snap b/test/config/__snapshots__/validation.spec.ts.snap index efe8cdf624bfafc242834e6761f8f8b4c90c528e..478ea19788835a07c67898f7184f618211aaf82e 100644 --- a/test/config/__snapshots__/validation.spec.ts.snap +++ b/test/config/__snapshots__/validation.spec.ts.snap @@ -57,11 +57,11 @@ exports[`config/validation validateConfig(config) errors for unsafe fileMatches Array [ Object { "depName": "Configuration Error", - "message": "Invalid regExp for npm.fileMatch: \`abc ([a-z]+) ([a-z]+))\`", + "message": "Invalid regExp for docker.fileMatch: \`x?+\`", }, Object { "depName": "Configuration Error", - "message": "Unsafe regExp for docker.fileMatch: \`(x+x+)+y\`", + "message": "Invalid regExp for npm.fileMatch: \`abc ([a-z]+) ([a-z]+))\`", }, ] `; @@ -113,7 +113,7 @@ Array [ }, Object { "depName": "Configuration Error", - "message": "Unsafe regExp for packageRules[0].excludePackagePatterns: \`(x+x+)+y\`", + "message": "Invalid regExp for packageRules[0].excludePackagePatterns: \`abc ([a-z]+) ([a-z]+))\`", }, ] `; diff --git a/test/config/validation.spec.ts b/test/config/validation.spec.ts index 38f4ad7be99b70e75bd8c67187a2125a65c38b69..ca5bc93b46f5332fad75c420e4304c25e1dc0f3f 100644 --- a/test/config/validation.spec.ts +++ b/test/config/validation.spec.ts @@ -20,7 +20,7 @@ describe('config/validation', () => { packageRules: [ { packagePatterns: ['*'], - excludePackagePatterns: ['(x+x+)+y'], + excludePackagePatterns: ['abc ([a-z]+) ([a-z]+))'], }, ], lockFileMaintenance: { @@ -138,13 +138,14 @@ describe('config/validation', () => { expect(errors).toMatchSnapshot(); expect(errors).toHaveLength(0); }); + it('errors for unsafe fileMatches', async () => { const config = { npm: { fileMatch: ['abc ([a-z]+) ([a-z]+))'], }, docker: { - fileMatch: ['(x+x+)+y'], + fileMatch: ['x?+'], }, }; const { warnings, errors } = await configValidation.validateConfig( @@ -154,5 +155,17 @@ describe('config/validation', () => { expect(errors).toHaveLength(2); 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); + }); }); });