From 6f46153e09e00e230d728a5b5108a0aa0429533f Mon Sep 17 00:00:00 2001 From: Rhys Arkins Date: Sun, 1 Mar 2020 20:52:43 +0100 Subject: [PATCH] fix(auto-replace): use regex replace to avoid infinite loops --- lib/workers/branch/auto-replace.ts | 43 +++++++++++++++--------- test/workers/branch/auto-replace.spec.ts | 6 ---- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/lib/workers/branch/auto-replace.ts b/lib/workers/branch/auto-replace.ts index 710ec4b2e4..8724207ef6 100644 --- a/lib/workers/branch/auto-replace.ts +++ b/lib/workers/branch/auto-replace.ts @@ -2,6 +2,7 @@ import { logger } from '../../logger'; import { get } from '../../manager'; import { WORKER_FILE_UPDATE_FAILED } from '../../constants/error-messages'; import { matchAt, replaceAt } from '../../util/string'; +import { regEx } from '../../util/regex'; export async function confirmIfDepUpdated( upgrade, @@ -59,6 +60,10 @@ export async function checkBranchDepsMatchBaseDeps( } } +function escapeRegExp(input: string): string { + return input.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + export async function doAutoReplace( upgrade, existingContent: string, @@ -77,27 +82,33 @@ export async function doAutoReplace( return existingContent; } const { + depName, currentValue, newValue, currentDigest, newDigest, - autoReplaceData, + autoReplaceData = {}, } = upgrade; const replaceString = autoReplaceData.replaceString || currentValue; + logger.trace({ depName, replaceString }, 'autoReplace replaceString'); + let searchIndex = existingContent.indexOf(replaceString); + if (searchIndex === -1) { + logger.error( + { depName }, + 'Cannot find replaceString in current file content' + ); + throw new Error(WORKER_FILE_UPDATE_FAILED); + } try { - let newString = replaceString; - do { - newString = newString.replace(currentValue, newValue); - } while (newString.includes(currentValue)); - if (currentDigest) { - do { - newString = newString.replace(currentDigest, newDigest); - } while (newString.includes(currentDigest)); - } - let searchIndex = existingContent.indexOf(replaceString); - if (searchIndex === -1) { - logger.error('Cannot find replaceString in current file content'); - throw new Error(WORKER_FILE_UPDATE_FAILED); + let newString = replaceString.replace( + regEx(escapeRegExp(currentValue), 'g'), + newValue + ); + if (currentDigest && newDigest) { + newString = newString.replace( + regEx(escapeRegExp(currentDigest), 'g'), + newDigest + ); } logger.debug(`Starting search at index ${searchIndex}`); // Iterate through the rest of the file @@ -117,9 +128,9 @@ export async function doAutoReplace( } } } - } catch (err) { + } catch (err) /* istanbul ignore next */ { logger.debug({ err }, 'doAutoReplace error'); } - logger.error('Could not autoReplace'); + // istanbul ignore next throw new Error(WORKER_FILE_UPDATE_FAILED); } diff --git a/test/workers/branch/auto-replace.spec.ts b/test/workers/branch/auto-replace.spec.ts index 61997a6e0e..b81f8b79e5 100644 --- a/test/workers/branch/auto-replace.spec.ts +++ b/test/workers/branch/auto-replace.spec.ts @@ -21,11 +21,6 @@ describe('workers/branch/auto-replace', () => { }; parentBranch = undefined; }); - it('throws on error', async () => { - await expect( - doAutoReplace(upgrade, 'existing content', parentBranch) - ).rejects.toThrow(); - }); it('rebases if the deps list has changed', async () => { upgrade.baseDeps = extractPackageFile(sampleHtml).deps; parentBranch = 'some existing branch'; @@ -54,7 +49,6 @@ describe('workers/branch/auto-replace', () => { upgrade.newValue = '7.1.1'; upgrade.autoReplaceData = { depIndex: 0, - replaceString: script, }; const res = await doAutoReplace(upgrade, src, parentBranch); expect(res).toMatchSnapshot();