diff --git a/lib/util/emoji.ts b/lib/util/emoji.ts
index 9e2a8b73ea93e96d27fc83c56199c286c8f87f90..7f6edaa8de1689a93c07c4ed3f03d9408e38b83c 100644
--- a/lib/util/emoji.ts
+++ b/lib/util/emoji.ts
@@ -55,7 +55,7 @@ export function emojify(text: string): string {
 const emojiRegexSrc = [emojibaseEmojiRegex, mathiasBynensEmojiRegex()].map(
   ({ source }) => source
 );
-const emojiRegex = new RegExp(`(?:${emojiRegexSrc.join('|')})`, 'g');
+const emojiRegex = new RegExp(`(?:${emojiRegexSrc.join('|')})`, 'g'); // TODO #12070
 const excludedModifiers = new Set([
   '20E3',
   '200D',
diff --git a/lib/util/exec/docker/index.ts b/lib/util/exec/docker/index.ts
index a6a160653dce6e5ea265b683b3405f3656b6e4fc..50ca989205ca8e7a3e5695b794cb25fc70ee8b1f 100644
--- a/lib/util/exec/docker/index.ts
+++ b/lib/util/exec/docker/index.ts
@@ -4,6 +4,7 @@ import { SYSTEM_INSUFFICIENT_MEMORY } from '../../../constants/error-messages';
 import { getPkgReleases } from '../../../datasource';
 import { logger } from '../../../logger';
 import * as versioning from '../../../versioning';
+import { regEx } from '../../regex';
 import { ensureTrailingSlash } from '../../url';
 import {
   DockerOptions,
@@ -119,7 +120,7 @@ export async function getDockerTag(
 }
 
 function getContainerName(image: string, prefix?: string): string {
-  return `${prefix || 'renovate_'}${image}`.replace(/\//g, '_');
+  return `${prefix || 'renovate_'}${image}`.replace(regEx(/\//g), '_');
 }
 
 function getContainerLabel(prefix: string): string {
@@ -257,7 +258,7 @@ export async function generateDockerCommand(
     ...commands,
     ...prepareCommands(postCommands),
   ].join(' && ');
-  result.push(`bash -l -c "${bashCommand.replace(/"/g, '\\"')}"`); // lgtm [js/incomplete-sanitization]
+  result.push(`bash -l -c "${bashCommand.replace(regEx(/"/g), '\\"')}"`); // lgtm [js/incomplete-sanitization]
 
   return result.join(' ');
 }
diff --git a/lib/util/git/author.ts b/lib/util/git/author.ts
index 5fe82349f1ef15284c32924e7c820bb8ad11e103..f029ebc3363762127d81232736ff97d616182b9c 100644
--- a/lib/util/git/author.ts
+++ b/lib/util/git/author.ts
@@ -1,5 +1,6 @@
 import addrs from 'email-addresses';
 import { logger } from '../../logger';
+import { regEx } from '../regex';
 
 export interface GitAuthor {
   name?: string;
@@ -20,7 +21,7 @@ export function parseGitAuthor(input: string): GitAuthor | null {
     let massagedBotEmail = false;
     if (input.includes('<') && input.includes('>')) {
       // try wrapping the name part in quotations
-      massagedInput = '"' + input.replace(/(\s?<)/, '"$1');
+      massagedInput = '"' + input.replace(regEx(/(\s?<)/), '"$1');
     }
     if (input.includes('[bot]@')) {
       // invalid github app/bot addresses
@@ -33,7 +34,7 @@ export function parseGitAuthor(input: string): GitAuthor | null {
     const parsed = addrs.parseOneAddress(massagedInput) as addrs.ParsedMailbox;
     if (parsed?.address) {
       result = {
-        name: parsed.name || input.replace(/@.*/, ''),
+        name: parsed.name || input.replace(regEx(/@.*/), ''),
         address: parsed.address,
       };
       if (massagedBotEmail) {
diff --git a/lib/util/git/index.ts b/lib/util/git/index.ts
index bf6ea96a1bbccd04e9c6e249aa0b079560ab5306..0422aecf0eb6576a45c194dd1db1aa704971ef94 100644
--- a/lib/util/git/index.ts
+++ b/lib/util/git/index.ts
@@ -24,6 +24,7 @@ import { logger } from '../../logger';
 import { ExternalHostError } from '../../types/errors/external-host-error';
 import { GitOptions, GitProtocol } from '../../types/git';
 import { Limit, incLimitedValue } from '../../workers/global/limits';
+import { regEx } from '../regex';
 import { parseGitAuthor } from './author';
 import { GitNoVerifyOption, getNoVerify, simpleGitConfig } from './config';
 import { configSigningKey, writePrivateKey } from './private-key';
@@ -120,7 +121,7 @@ function checkForPlatformFailure(err: Error): void {
 }
 
 function localName(branchName: string): string {
-  return branchName.replace(/^origin\//, '');
+  return branchName.replace(regEx(/^origin\//), ''); // TODO #12071
 }
 
 async function isDirectory(dir: string): Promise<boolean> {
@@ -168,7 +169,7 @@ async function fetchBranchCommits(): Promise<void> {
     (await git.raw(opts))
       .split('\n')
       .filter(Boolean)
-      .map((line) => line.trim().split(/\s+/))
+      .map((line) => line.trim().split(regEx(/\s+/))) // TODO #12071
       .forEach(([sha, ref]) => {
         config.branchCommits[ref.replace('refs/heads/', '')] = sha;
       });
@@ -272,7 +273,7 @@ export async function getSubmodules(): Promise<string[]> {
       ])) || ''
     )
       .trim()
-      .split(/[\n\s]/)
+      .split(regEx(/[\n\s]/))
       .filter((_e: string, i: number) => i % 2);
   } catch (err) /* istanbul ignore next */ {
     logger.warn({ err }, 'Error getting submodules');
@@ -459,7 +460,7 @@ export async function getFileList(): Promise<string[]> {
     .split('\n')
     .filter(Boolean)
     .filter((line) => line.startsWith('100'))
-    .map((line) => line.split(/\t/).pop())
+    .map((line) => line.split(regEx(/\t/)).pop()) // TODO #12071
     .filter((file: string) =>
       submodules.every((submodule: string) => !file.startsWith(submodule))
     );
diff --git a/lib/util/git/url.ts b/lib/util/git/url.ts
index 881de8722b3c6fc1aec98a485e6e58a96c83f02e..e7a6ef0d3093fa1c4095a72f57ed2c6535591394 100644
--- a/lib/util/git/url.ts
+++ b/lib/util/git/url.ts
@@ -1,13 +1,14 @@
 import GitUrlParse from 'git-url-parse';
 import { logger } from '../../logger';
 import * as hostRules from '../host-rules';
+import { regEx } from '../regex';
 
 export function getHttpUrl(url: string, token?: string): string {
   const parsedUrl = GitUrlParse(url);
 
   parsedUrl.token = token;
 
-  const protocol = /^https?$/.exec(parsedUrl.protocol)
+  const protocol = regEx(/^https?$/).exec(parsedUrl.protocol) // TODO #12071
     ? parsedUrl.protocol
     : 'https';
   return parsedUrl.toString(protocol);
diff --git a/lib/util/http/gitea.ts b/lib/util/http/gitea.ts
index ff53983fc856d72d166f0e7503452891f36186dd..2631c5e0d7ba713a4a1d487e37a1c82c1fe830fb 100644
--- a/lib/util/http/gitea.ts
+++ b/lib/util/http/gitea.ts
@@ -2,9 +2,9 @@ import { PlatformId } from '../../constants';
 import { resolveBaseUrl } from '../url';
 import { Http, HttpOptions, HttpResponse, InternalHttpOptions } from '.';
 
-let baseUrl;
+let baseUrl: string;
 export const setBaseUrl = (newBaseUrl: string): void => {
-  baseUrl = newBaseUrl.replace(/\/*$/, '/');
+  baseUrl = newBaseUrl.replace(/\/*$/, '/'); // TODO #12070 #12071
 };
 
 export interface GiteaHttpOptions extends InternalHttpOptions {
diff --git a/lib/util/http/github.ts b/lib/util/http/github.ts
index ec267a4f5ad29d8d3cb30953f05d321b4f878034..daba1c8b632b3a996c3a7c0331b85e6e7981beb4 100644
--- a/lib/util/http/github.ts
+++ b/lib/util/http/github.ts
@@ -11,6 +11,7 @@ import {
 import { logger } from '../../logger';
 import { ExternalHostError } from '../../types/errors/external-host-error';
 import { maskToken } from '../mask';
+import { regEx } from '../regex';
 import { GotLegacyError } from './legacy';
 import { Http, HttpPostOptions, HttpResponse, InternalHttpOptions } from '.';
 
@@ -153,7 +154,8 @@ interface GraphqlOptions {
 
 function constructAcceptString(input?: any): string {
   const defaultAccept = 'application/vnd.github.v3+json';
-  const acceptStrings = typeof input === 'string' ? input.split(/\s*,\s*/) : [];
+  const acceptStrings =
+    typeof input === 'string' ? input.split(regEx(/\s*,\s*/)) : [];
   if (
     !acceptStrings.some((x) => x.startsWith('application/vnd.github.')) ||
     acceptStrings.length < 2
diff --git a/lib/util/ignore.ts b/lib/util/ignore.ts
index b6d6f41f6a4cee92763487c9a7615e85d1627481..6ef218d970996c24a5303b909ca04fc0414f1b8a 100644
--- a/lib/util/ignore.ts
+++ b/lib/util/ignore.ts
@@ -1,7 +1,9 @@
 import { logger } from '../logger';
+import { regEx } from './regex';
 
 export function isSkipComment(comment?: string): boolean {
-  if (/^(renovate|pyup):/.test(comment)) {
+  if (regEx(/^(renovate|pyup):/).test(comment)) {
+    // TODO #12070 #12071 needs to be checked manually
     const command = comment.split('#')[0].split(':')[1].trim();
     if (command === 'ignore') {
       return true;
diff --git a/lib/util/markdown.ts b/lib/util/markdown.ts
index 73648a7c4d76500d448c5d486cfae59fea1f166c..962f0cf11848b1479a5b0a72d2cff178dd6594f1 100644
--- a/lib/util/markdown.ts
+++ b/lib/util/markdown.ts
@@ -1,22 +1,23 @@
 import remark from 'remark';
 import github from 'remark-github';
+import { regEx } from './regex';
 
 // Generic replacements/link-breakers
 export function sanitizeMarkdown(markdown: string): string {
   let res = markdown;
   // Put a zero width space after every # followed by a digit
-  res = res.replace(/#(\d)/gi, '#&#8203;$1');
+  res = res.replace(regEx(/#(\d)/gi), '#&#8203;$1'); // TODO #12071
   // Put a zero width space after every @ symbol to prevent unintended hyperlinking
-  res = res.replace(/@/g, '@&#8203;');
-  res = res.replace(/(`\[?@)&#8203;/g, '$1');
-  res = res.replace(/([a-z]@)&#8203;/gi, '$1');
-  res = res.replace(/\/compare\/@&#8203;/g, '/compare/@');
-  res = res.replace(/(\(https:\/\/[^)]*?)\.\.\.@&#8203;/g, '$1...@');
-  res = res.replace(/([\s(])#(\d+)([)\s]?)/g, '$1#&#8203;$2$3');
+  res = res.replace(regEx(/@/g), '@&#8203;'); // TODO #12071
+  res = res.replace(regEx(/(`\[?@)&#8203;/g), '$1'); // TODO #12071
+  res = res.replace(regEx(/([a-z]@)&#8203;/gi), '$1'); // TODO #12071
+  res = res.replace(regEx(/\/compare\/@&#8203;/g), '/compare/@'); // TODO #12071
+  res = res.replace(regEx(/(\(https:\/\/[^)]*?)\.\.\.@&#8203;/g), '$1...@'); // TODO #12071
+  res = res.replace(regEx(/([\s(])#(\d+)([)\s]?)/g), '$1#&#8203;$2$3'); // TODO #12071
   // convert escaped backticks back to `
-  const backTickRe = /&#x60;([^/]*?)&#x60;/g;
+  const backTickRe = regEx(/&#x60;([^/]*?)&#x60;/g); // TODO #12071
   res = res.replace(backTickRe, '`$1`');
-  res = res.replace(/`#&#8203;(\d+)`/g, '`#$1`');
+  res = res.replace(regEx(/`#&#8203;(\d+)`/g), '`#$1`'); // TODO #12071
   return res;
 }
 
diff --git a/lib/util/modules.ts b/lib/util/modules.ts
index ba4bfe3625cb4f9d58bcb2ca118623085112a856..9d988ac1095be3abe5643ef378f2f3af6fd2fa19 100644
--- a/lib/util/modules.ts
+++ b/lib/util/modules.ts
@@ -1,9 +1,10 @@
 import fs from 'fs';
 import { join, normalizeTrim } from 'upath';
+import { regEx } from './regex';
 
 function relatePath(here: string, there: string): string {
-  const thereParts = normalizeTrim(there).split(/[\\/]/);
-  const hereParts = normalizeTrim(here).split(/[\\/]/);
+  const thereParts = normalizeTrim(there).split(regEx(/[\\/]/)); // TODO #12070 needs to be tested manually
+  const hereParts = normalizeTrim(here).split(regEx(/[\\/]/)); // TODO #12070 needs to be tested manually
 
   let idx = 0;
   while (
diff --git a/lib/util/package-rules.ts b/lib/util/package-rules.ts
index 1fa29033778434b7b950f1340569a6252dae4f01..2f21b91bcd611f406eb893e0f0cafa1245435a48 100644
--- a/lib/util/package-rules.ts
+++ b/lib/util/package-rules.ts
@@ -142,7 +142,7 @@ function matchesRule(
           packagePattern === '^*$' || packagePattern === '*'
             ? '.*'
             : packagePattern
-        );
+        ); // TODO #12071
         if (packageRegex.test(depName)) {
           logger.trace(`${depName} matches against ${String(packageRegex)}`);
           isMatch = true;
@@ -172,7 +172,7 @@ function matchesRule(
     for (const pattern of excludePackagePatterns) {
       const packageRegex = regEx(
         pattern === '^*$' || pattern === '*' ? '.*' : pattern
-      );
+      ); // TODO #12071
       if (packageRegex.test(depName)) {
         logger.trace(`${depName} matches against ${String(packageRegex)}`);
         isMatch = true;
diff --git a/lib/util/regex.ts b/lib/util/regex.ts
index 78d4814227a505c52dd042b8eaa6265ba533302f..19b203ea51204686b91c67c976563d60564633bd 100644
--- a/lib/util/regex.ts
+++ b/lib/util/regex.ts
@@ -29,11 +29,11 @@ export function regEx(pattern: string | RegExp, flags?: string): RegExp {
 }
 
 export function escapeRegExp(input: string): string {
-  return input.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
+  return input.replace(regEx(/[.*+\-?^${}()|[\]\\]/g), '\\$&'); // $& means the whole matched string // TODO #12071
 }
 
-const configValStart = /^!?\//;
-const configValEnd = /\/$/;
+const configValStart = regEx(/^!?\//);
+const configValEnd = regEx(/\/$/);
 
 export function isConfigRegex(input: unknown): input is string {
   return (
diff --git a/lib/util/template/index.ts b/lib/util/template/index.ts
index cde48e5796d4fed65a5d7922cbb78b279f4dc576..cb61a66b105587a9d37be15662dd8fdd3f61401b 100644
--- a/lib/util/template/index.ts
+++ b/lib/util/template/index.ts
@@ -7,8 +7,9 @@ import { clone } from '../clone';
 handlebars.registerHelper('encodeURIComponent', encodeURIComponent);
 
 // istanbul ignore next
-handlebars.registerHelper('replace', (find, replace, context) =>
-  context.replace(new RegExp(find, 'g'), replace)
+handlebars.registerHelper(
+  'replace',
+  (find, replace, context) => context.replace(new RegExp(find, 'g'), replace) // TODO #12070
 );
 
 export const exposedConfigOptions = [
@@ -139,7 +140,7 @@ function getFilteredObject(input: CompileInput): any {
   return res;
 }
 
-const templateRegex = /{{(#(if|unless) )?([a-zA-Z]+)}}/g;
+const templateRegex = /{{(#(if|unless) )?([a-zA-Z]+)}}/g; // TODO #12070
 
 export function compile(
   template: string,
diff --git a/lib/util/url.ts b/lib/util/url.ts
index 245e0be5fdfa93bc2fac43d86426ed160ddafa9f..070773525b85a430d99932b21c5544f9d8ca3a75 100644
--- a/lib/util/url.ts
+++ b/lib/util/url.ts
@@ -1,4 +1,5 @@
 import urlJoin from 'url-join';
+import { regEx } from './regex';
 
 export function joinUrlParts(...parts: string[]): string {
   return urlJoin(...parts);
@@ -14,11 +15,11 @@ export function ensurePathPrefix(url: string, prefix: string): string {
 }
 
 export function ensureTrailingSlash(url: string): string {
-  return url.replace(/\/?$/, '/');
+  return url.replace(/\/?$/, '/'); // TODO #12070 #12071 add tests for this one
 }
 
 export function trimTrailingSlash(url: string): string {
-  return url.replace(/\/+$/, '');
+  return url.replace(regEx(/\/+$/), ''); // TODO #12071
 }
 
 export function resolveBaseUrl(baseUrl: string, input: string | URL): string {
diff --git a/lib/versioning/cargo/index.ts b/lib/versioning/cargo/index.ts
index cc01043b3f265e0eb4f008c9ec7e369b5c626ea4..ee33955a0954ca898a6d490b60538206fd865997 100644
--- a/lib/versioning/cargo/index.ts
+++ b/lib/versioning/cargo/index.ts
@@ -1,4 +1,5 @@
 import { logger } from '../../logger';
+import { regEx } from '../../util/regex';
 import { api as npm } from '../npm';
 import type { NewValueConfig, VersioningApi } from '../types';
 
@@ -40,7 +41,7 @@ function npm2cargo(input: string): string {
   }
   // Note: this doesn't remove the ^
   const res = input
-    .split(/\s+,?\s*|\s*,?\s+/)
+    .split(regEx(/\s+,?\s*|\s*,?\s+/))
     .map((str) => str.trim())
     .filter(notEmpty);
   const operators = ['^', '~', '=', '>', '<', '<=', '>='];
diff --git a/lib/versioning/composer/index.ts b/lib/versioning/composer/index.ts
index fb47e5433a2f98074304e772cd303fd82a6686e2..226f826f188b694d399894794096ec56643e10a5 100644
--- a/lib/versioning/composer/index.ts
+++ b/lib/versioning/composer/index.ts
@@ -1,6 +1,7 @@
 import { coerce } from 'semver';
 import { parseRange } from 'semver-utils';
 import { logger } from '../../logger';
+import { regEx } from '../../util/regex';
 import { api as npm } from '../npm';
 import type { NewValueConfig, VersioningApi } from '../types';
 
@@ -43,7 +44,7 @@ function convertStabilityModifier(input: string): string {
 
   // 1.0@beta2 to 1.0-beta.2
   const stability = versionParts[1].replace(
-    /(?:^|\s)(beta|alpha|rc)([1-9][0-9]*)(?: |$)/gi,
+    regEx(/(?:^|\s)(beta|alpha|rc)([1-9][0-9]*)(?: |$)/gi),
     '$1.$2'
   );
 
@@ -54,7 +55,7 @@ function convertStabilityModifier(input: string): string {
 
 function normalizeVersion(input: string): string {
   let output = input;
-  output = output.replace(/(^|>|>=|\^|~)v/i, '$1');
+  output = output.replace(regEx(/(^|>|>=|\^|~)v/i), '$1');
   return convertStabilityModifier(output);
 }
 
@@ -70,9 +71,15 @@ function composer2npm(input: string): string {
   let output = versionId;
 
   // ~4 to ^4 and ~4.1 to ^4.1
-  output = output.replace(/(?:^|\s)~([1-9][0-9]*(?:\.[0-9]*)?)(?: |$)/g, '^$1');
+  output = output.replace(
+    regEx(/(?:^|\s)~([1-9][0-9]*(?:\.[0-9]*)?)(?: |$)/g),
+    '^$1'
+  );
   // ~0.4 to >=0.4 <1
-  output = output.replace(/(?:^|\s)~(0\.[1-9][0-9]*)(?: |$)/g, '>=$1 <1');
+  output = output.replace(
+    regEx(/(?:^|\s)~(0\.[1-9][0-9]*)(?: |$)/g),
+    '>=$1 <1'
+  );
 
   return output + stability;
 }
@@ -141,7 +148,7 @@ function getNewValue({
   let newValue: string;
   if (isVersion(currentValue)) {
     newValue = newVersion;
-  } else if (/^[~^](0\.[1-9][0-9]*)$/.test(currentValue)) {
+  } else if (regEx(/^[~^](0\.[1-9][0-9]*)$/).test(currentValue)) {
     const operator = currentValue.substr(0, 1);
     // handle ~0.4 case first
     if (toMajor === 0) {
@@ -149,11 +156,11 @@ function getNewValue({
     } else {
       newValue = `${operator}${toMajor}.0`;
     }
-  } else if (/^[~^]([0-9]*)$/.test(currentValue)) {
+  } else if (regEx(/^[~^]([0-9]*)$/).test(currentValue)) {
     // handle ~4 case
     const operator = currentValue.substr(0, 1);
     newValue = `${operator}${toMajor}`;
-  } else if (/^[~^]([0-9]*(?:\.[0-9]*)?)$/.test(currentValue)) {
+  } else if (regEx(/^[~^]([0-9]*(?:\.[0-9]*)?)$/).test(currentValue)) {
     const operator = currentValue.substr(0, 1);
     // handle ~4.1 case
     if (currentVersion && toMajor > getMajor(currentVersion)) {
@@ -212,7 +219,7 @@ function getNewValue({
     newValue = newVersion;
   }
   if (currentValue.split('.')[0].includes('v')) {
-    newValue = newValue.replace(/([0-9])/, 'v$1');
+    newValue = newValue.replace(regEx(/([0-9])/), 'v$1');
   }
 
   // Preserve original min-stability specifier
diff --git a/lib/versioning/docker/index.ts b/lib/versioning/docker/index.ts
index 874ccb335708844592f707ba1dfad7d740b4fff6..1ec88bb36ed330f993b69f373795d997160e1f12 100644
--- a/lib/versioning/docker/index.ts
+++ b/lib/versioning/docker/index.ts
@@ -1,3 +1,4 @@
+import { regEx } from '../../util/regex';
 import * as generic from '../loose/generic';
 import type { VersioningApi } from '../types';
 
@@ -8,15 +9,15 @@ export const urls = [
 ];
 export const supportsRanges = false;
 
-const versionPattern = /^(?<version>\d+(?:\.\d+)*)(?<prerelease>.*)$/;
-const commitHashPattern = /^[a-f0-9]{7,40}$/;
-const numericPattern = /^[0-9]+$/;
+const versionPattern = regEx(/^(?<version>\d+(?:\.\d+)*)(?<prerelease>.*)$/);
+const commitHashPattern = regEx(/^[a-f0-9]{7,40}$/);
+const numericPattern = regEx(/^[0-9]+$/);
 
 function parse(version: string): generic.GenericVersion {
   if (commitHashPattern.test(version) && !numericPattern.test(version)) {
     return null;
   }
-  const versionPieces = version.replace(/^v/, '').split('-');
+  const versionPieces = version.replace(regEx(/^v/), '').split('-');
   const prefix = versionPieces.shift();
   const suffix = versionPieces.join('-');
   const m = versionPattern.exec(prefix);
diff --git a/lib/versioning/gradle/compare.ts b/lib/versioning/gradle/compare.ts
index abb1e25fc0cdc22ecbd049820ceed218dbbda742..bd5c85bfb08053b6b641d5d6cd9405cf2897b910 100644
--- a/lib/versioning/gradle/compare.ts
+++ b/lib/versioning/gradle/compare.ts
@@ -1,3 +1,5 @@
+import { regEx } from '../../util/regex';
+
 export enum TokenType {
   Number = 1,
   String,
@@ -20,11 +22,11 @@ function iterateChars(str: string, cb: (p: string, n: string) => void): void {
 }
 
 function isSeparator(char: string): boolean {
-  return /^[-._+]$/i.test(char);
+  return regEx(/^[-._+]$/i).test(char);
 }
 
 function isDigit(char: string): boolean {
-  return /^\d$/.test(char);
+  return regEx(/^\d$/).test(char);
 }
 
 function isLetter(char: string): boolean {
@@ -48,7 +50,7 @@ export function tokenize(versionStr: string): Token[] | null {
     }
     if (result) {
       const val = currentVal;
-      if (/^\d+$/.test(val)) {
+      if (regEx(/^\d+$/).test(val)) {
         result.push({
           type: TokenType.Number,
           val: parseInt(val, 10),
@@ -191,11 +193,11 @@ export function isVersion(input: string): boolean {
     return false;
   }
 
-  if (!/^[-._+a-zA-Z0-9]+$/i.test(input)) {
+  if (!regEx(/^[-._+a-zA-Z0-9]+$/i).test(input)) {
     return false;
   }
 
-  if (/^latest\.?/i.test(input)) {
+  if (regEx(/^latest\.?/i).test(input)) {
     return false;
   }
 
@@ -231,9 +233,9 @@ export function parsePrefixRange(input: string): PrefixRange | null {
     return { tokens: [] };
   }
 
-  const postfixRegex = /[-._]\+$/;
+  const postfixRegex = regEx(/[-._]\+$/);
   if (postfixRegex.test(input)) {
-    const prefixValue = input.replace(/[-._]\+$/, '');
+    const prefixValue = input.replace(regEx(/[-._]\+$/), '');
     const tokens = tokenize(prefixValue);
     return tokens ? { tokens } : null;
   }
@@ -241,8 +243,9 @@ export function parsePrefixRange(input: string): PrefixRange | null {
   return null;
 }
 
-const mavenBasedRangeRegex =
-  /^(?<leftBoundStr>[[\](]\s*)(?<leftVal>[-._+a-zA-Z0-9]*?)(?<separator>\s*,\s*)(?<rightVal>[-._+a-zA-Z0-9]*?)(?<rightBoundStr>\s*[[\])])$/;
+const mavenBasedRangeRegex = regEx(
+  /^(?<leftBoundStr>[[\](]\s*)(?<leftVal>[-._+a-zA-Z0-9]*?)(?<separator>\s*,\s*)(?<rightVal>[-._+a-zA-Z0-9]*?)(?<rightBoundStr>\s*[[\])])$/
+);
 
 export function parseMavenBasedRange(input: string): MavenBasedRange | null {
   if (!input) {
diff --git a/lib/versioning/gradle/index.ts b/lib/versioning/gradle/index.ts
index d72d9d4cd3e399f9fe53477c1a1c9b8127fcfcb7..990f3d7ff7314f810cc88e02900b1afe9892abf1 100644
--- a/lib/versioning/gradle/index.ts
+++ b/lib/versioning/gradle/index.ts
@@ -1,3 +1,4 @@
+import { regEx } from '../../util/regex';
 import type { NewValueConfig, VersioningApi } from '../types';
 import {
   RangeBound,
@@ -22,7 +23,7 @@ const equals = (a: string, b: string): boolean => compare(a, b) === 0;
 
 const getMajor = (version: string): number | null => {
   if (isVersion(version)) {
-    const tokens = tokenize(version.replace(/^v/i, ''));
+    const tokens = tokenize(version.replace(regEx(/^v/i), ''));
     const majorToken = tokens[0];
     if (majorToken && majorToken.type === TokenType.Number) {
       return +majorToken.val;
@@ -33,7 +34,7 @@ const getMajor = (version: string): number | null => {
 
 const getMinor = (version: string): number | null => {
   if (isVersion(version)) {
-    const tokens = tokenize(version.replace(/^v/i, ''));
+    const tokens = tokenize(version.replace(regEx(/^v/i), ''));
     const majorToken = tokens[0];
     const minorToken = tokens[1];
     if (
@@ -51,7 +52,7 @@ const getMinor = (version: string): number | null => {
 
 const getPatch = (version: string): number | null => {
   if (isVersion(version)) {
-    const tokens = tokenize(version.replace(/^v/i, ''));
+    const tokens = tokenize(version.replace(regEx(/^v/i), ''));
     const majorToken = tokens[0];
     const minorToken = tokens[1];
     const patchToken = tokens[2];
diff --git a/lib/versioning/hashicorp/index.ts b/lib/versioning/hashicorp/index.ts
index b5890b0dea3be87eecaf477955e5ec9b68204fd3..79749443b08a0e8f86ec3525e74ceac0c2a877f2 100644
--- a/lib/versioning/hashicorp/index.ts
+++ b/lib/versioning/hashicorp/index.ts
@@ -1,3 +1,4 @@
+import { regEx } from '../../util/regex';
 import { api as npm } from '../npm';
 import type { NewValueConfig, VersioningApi } from '../types';
 
@@ -11,7 +12,7 @@ export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];
 
 function hashicorp2npm(input: string): string {
   // The only case incompatible with semver is a "short" ~>, e.g. ~> 1.2
-  return input.replace(/~>(\s*\d+\.\d+$)/, '^$1').replace(',', '');
+  return input.replace(regEx(/~>(\s*\d+\.\d+$)/), '^$1').replace(',', '');
 }
 
 const isLessThanRange = (version: string, range: string): boolean =>
@@ -36,8 +37,11 @@ function getNewValue({
   newVersion,
 }: NewValueConfig): string {
   if (['replace', 'update-lockfile'].includes(rangeStrategy)) {
-    if (/~>\s*0\.\d+/.test(currentValue) && npm.getMajor(newVersion) === 0) {
-      const testFullVersion = /(~>\s*0\.)(\d+)\.\d$/;
+    if (
+      regEx(/~>\s*0\.\d+/).test(currentValue) &&
+      npm.getMajor(newVersion) === 0
+    ) {
+      const testFullVersion = regEx(/(~>\s*0\.)(\d+)\.\d$/);
       let replaceValue = '';
       if (testFullVersion.test(currentValue)) {
         replaceValue = `$<prefix>${npm.getMinor(newVersion)}.0`;
@@ -45,14 +49,14 @@ function getNewValue({
         replaceValue = `$<prefix>${npm.getMinor(newVersion)}$<suffix>`;
       }
       return currentValue.replace(
-        /(?<prefix>~>\s*0\.)\d+(?<suffix>.*)$/,
+        /(?<prefix>~>\s*0\.)\d+(?<suffix>.*)$/, // TODO #12070
         replaceValue
       );
     }
     // handle special ~> 1.2 case
-    if (/(~>\s*)\d+\.\d+$/.test(currentValue)) {
+    if (regEx(/(~>\s*)\d+\.\d+$/).test(currentValue)) {
       return currentValue.replace(
-        /(?<prefix>~>\s*)\d+\.\d+$/,
+        /(?<prefix>~>\s*)\d+\.\d+$/, // TODO #12070
         `$<prefix>${npm.getMajor(newVersion)}.0`
       );
     }
diff --git a/lib/versioning/hex/index.ts b/lib/versioning/hex/index.ts
index 10e475f605c33a452cddaea20aecadf9d77ffbd8..828837720ba36e60f58005af92dc2dfe03aa547c 100644
--- a/lib/versioning/hex/index.ts
+++ b/lib/versioning/hex/index.ts
@@ -1,3 +1,4 @@
+import { regEx } from '../../util/regex';
 import { api as npm } from '../npm';
 import type { NewValueConfig, VersioningApi } from '../types';
 
@@ -9,11 +10,11 @@ export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];
 
 function hex2npm(input: string): string {
   return input
-    .replace(/~>\s*(\d+\.\d+)$/, '^$1')
-    .replace(/~>\s*(\d+\.\d+\.\d+)/, '~$1')
-    .replace(/==|and/, '')
+    .replace(regEx(/~>\s*(\d+\.\d+)$/), '^$1') // TODO #12071
+    .replace(regEx(/~>\s*(\d+\.\d+\.\d+)/), '~$1') // TODO #12071
+    .replace(regEx(/==|and/), '') // TODO #12071
     .replace('or', '||')
-    .replace(/!=\s*(\d+\.\d+(\.\d+.*)?)/, '>$1 <$1')
+    .replace(regEx(/!=\s*(\d+\.\d+(\.\d+.*)?)/), '>$1 <$1') // TODO #12071
     .trim();
 }
 
@@ -69,18 +70,18 @@ const getNewValue = ({
     newVersion,
   });
   newSemver = npm2hex(newSemver);
-  if (/~>\s*(\d+\.\d+\.\d+)$/.test(currentValue)) {
+  if (regEx(/~>\s*(\d+\.\d+\.\d+)$/).test(currentValue)) {
     newSemver = newSemver.replace(
-      /[\^~]\s*(\d+\.\d+\.\d+)/,
+      regEx(/[\^~]\s*(\d+\.\d+\.\d+)/),
       (_str, p1: string) => `~> ${p1}`
     );
-  } else if (/~>\s*(\d+\.\d+)$/.test(currentValue)) {
+  } else if (regEx(/~>\s*(\d+\.\d+)$/).test(currentValue)) {
     newSemver = newSemver.replace(
-      /\^\s*(\d+\.\d+)(\.\d+)?/,
+      regEx(/\^\s*(\d+\.\d+)(\.\d+)?/),
       (_str, p1: string) => `~> ${p1}`
     );
   } else {
-    newSemver = newSemver.replace(/~\s*(\d+\.\d+\.\d)/, '~> $1');
+    newSemver = newSemver.replace(regEx(/~\s*(\d+\.\d+\.\d)/), '~> $1');
   }
   if (npm.isVersion(newSemver)) {
     newSemver = `== ${newSemver}`;
diff --git a/lib/versioning/ivy/parse.ts b/lib/versioning/ivy/parse.ts
index 568bdb5eff3202b06b5e333ad70e24282c4d10e7..94e9d2cf351b1bc0f9519a4fd77652a89a8be2b5 100644
--- a/lib/versioning/ivy/parse.ts
+++ b/lib/versioning/ivy/parse.ts
@@ -1,3 +1,4 @@
+import { regEx } from '../../util/regex';
 import { isSingleVersion, parseRange, rangeToStr } from '../maven/compare';
 
 const REV_TYPE_LATEST = 'REV_TYPE_LATEST';
@@ -10,7 +11,7 @@ export interface Revision {
   value: string;
 }
 
-export const LATEST_REGEX = /^latest\.|^latest$/i;
+export const LATEST_REGEX = regEx(/^latest\.|^latest$/i);
 
 function parseDynamicRevision(str: string): Revision | null {
   if (!str) {
@@ -25,7 +26,7 @@ function parseDynamicRevision(str: string): Revision | null {
     };
   }
 
-  const SUBREV_REGEX = /\.\+$/;
+  const SUBREV_REGEX = regEx(/\.\+$/);
   if (str.endsWith('.+')) {
     const value = str.replace(SUBREV_REGEX, '');
     if (isSingleVersion(value)) {
diff --git a/lib/versioning/loose/index.ts b/lib/versioning/loose/index.ts
index 9b0ba7723502cf9028da64dabffbd6c375690ae1..32cba9301f3fb086570fdd755e3075d893b2004f 100644
--- a/lib/versioning/loose/index.ts
+++ b/lib/versioning/loose/index.ts
@@ -1,3 +1,4 @@
+import { regEx } from '../../util/regex';
 import type { VersioningApi } from '../types';
 import * as generic from './generic';
 
@@ -6,9 +7,9 @@ export const displayName = 'Loose';
 export const urls = [];
 export const supportsRanges = false;
 
-const versionPattern = /^v?(\d+(?:\.\d+)*)(.*)$/;
-const commitHashPattern = /^[a-f0-9]{7,40}$/;
-const numericPattern = /^[0-9]+$/;
+const versionPattern = regEx(/^v?(\d+(?:\.\d+)*)(.*)$/);
+const commitHashPattern = regEx(/^[a-f0-9]{7,40}$/);
+const numericPattern = regEx(/^[0-9]+$/);
 
 function parse(version: string): any {
   if (commitHashPattern.test(version) && !numericPattern.test(version)) {
diff --git a/lib/versioning/maven/compare.ts b/lib/versioning/maven/compare.ts
index cf3980772a6db256cd5030b98123fa6b558e901f..308ff9609b45243c16e3d0e8837dd9ea2dab9902 100644
--- a/lib/versioning/maven/compare.ts
+++ b/lib/versioning/maven/compare.ts
@@ -1,3 +1,5 @@
+import { regEx } from '../../util/regex';
+
 const PREFIX_DOT = 'PREFIX_DOT';
 const PREFIX_HYPHEN = 'PREFIX_HYPHEN';
 
@@ -35,11 +37,11 @@ function iterateChars(str: string, cb: (p: string, n: string) => void): void {
 }
 
 function isDigit(char: string): boolean {
-  return /^\d$/.test(char);
+  return regEx(/^\d$/).test(char);
 }
 
 function isLetter(char: string): boolean {
-  return /^[a-z]$/i.test(char);
+  return regEx(/^[a-z]$/i).test(char);
 }
 
 function isTransition(prevChar: string, nextChar: string): boolean {
@@ -55,7 +57,7 @@ function iterateTokens(versionStr: string, cb: (token: Token) => void): void {
 
   function yieldToken(transition = false): void {
     const val = currentVal || '0';
-    if (/^\d+$/.test(val)) {
+    if (regEx(/^\d+$/).test(val)) {
       cb({
         prefix: currentPrefix,
         type: TYPE_NUMBER,
@@ -117,7 +119,7 @@ function tokenize(versionStr: string, preserveMinorZeroes = false): Token[] {
   let buf: Token[] = [];
   let result: Token[] = [];
   let leadingZero = true;
-  iterateTokens(versionStr.toLowerCase().replace(/^v/i, ''), (token) => {
+  iterateTokens(versionStr.toLowerCase().replace(regEx(/^v/i), ''), (token) => {
     if (token.prefix === PREFIX_HYPHEN) {
       buf = [];
     }
@@ -276,13 +278,13 @@ function isVersion(version: string): boolean {
   if (!version) {
     return false;
   }
-  if (!/^[a-z0-9.-]+$/i.test(version)) {
+  if (!regEx(/^[a-z0-9.-]+$/i).test(version)) {
     return false;
   }
-  if (/^[.-]/.test(version)) {
+  if (regEx(/^[.-]/).test(version)) {
     return false;
   }
-  if (/[.-]$/.test(version)) {
+  if (regEx(/[.-]$/).test(version)) {
     return false;
   }
   if (['latest', 'release'].includes(version.toLowerCase())) {
@@ -316,7 +318,7 @@ function parseRange(rangeStr: string): Range[] {
       return;
     }
     if (interval.leftType === null) {
-      if (/^\[.*]$/.test(subStr)) {
+      if (regEx(/^\[.*]$/).test(subStr)) {
         const ver = subStr.slice(1, -1);
         result.push({
           leftType: INCLUDING_POINT,
diff --git a/lib/versioning/npm/range.ts b/lib/versioning/npm/range.ts
index e4e6027cbfa8100ab0fd3391c70490a040780de0..fb4d15815ec2c079874b85d6f57933ab18a0ef83 100644
--- a/lib/versioning/npm/range.ts
+++ b/lib/versioning/npm/range.ts
@@ -9,6 +9,7 @@ import {
 } from 'semver';
 import { parseRange } from 'semver-utils';
 import { logger } from '../../logger';
+import { regEx } from '../../util/regex';
 import type { NewValueConfig } from '../types';
 
 function replaceCaretValue(oldValue: string, newValue: string): string {
@@ -234,7 +235,7 @@ export function getNewValue({
       res = `<${toVersionMajor + 1}`;
     }
     if (currentValue.includes('< ')) {
-      res = res.replace(/</g, '< ');
+      res = res.replace(regEx(/</g), '< ');
     }
     return res;
   }
diff --git a/lib/versioning/nuget/index.ts b/lib/versioning/nuget/index.ts
index 387aad98a5af4c3a1baa6d24326951657b7a53f5..0c94eccdb78c8327c65cbabd9cef91c20ad7cfc6 100644
--- a/lib/versioning/nuget/index.ts
+++ b/lib/versioning/nuget/index.ts
@@ -1,3 +1,4 @@
+import { regEx } from '../../util/regex';
 import * as generic from '../loose/generic';
 import type { VersioningApi } from '../types';
 
@@ -8,7 +9,7 @@ export const urls = [
 ];
 export const supportsRanges = false;
 
-const pattern = /^(\d+(?:\.\d+)*)(-[^+]+)?(\+.*)?$/;
+const pattern = regEx(/^(\d+(?:\.\d+)*)(-[^+]+)?(\+.*)?$/);
 
 function parse(version: string): any {
   const matches = pattern.exec(version);
diff --git a/lib/versioning/pep440/range.ts b/lib/versioning/pep440/range.ts
index c2c9d85627927aeca67b88fd2bac5cf33b98a422..9e7785a9e09c85cd33e4e13c504afc68ef7798d9 100644
--- a/lib/versioning/pep440/range.ts
+++ b/lib/versioning/pep440/range.ts
@@ -2,6 +2,7 @@ import { gte, lt, lte, satisfies } from '@renovate/pep440';
 import { parse as parseRange } from '@renovate/pep440/lib/specifier';
 import { parse as parseVersion } from '@renovate/pep440/lib/version';
 import { logger } from '../../logger';
+import { regEx } from '../../util/regex';
 import type { NewValueConfig } from '../types';
 
 function getFutureVersion(
@@ -140,7 +141,7 @@ export function getNewValue({
     .join(', ');
 
   if (result.includes(', ') && !currentValue.includes(', ')) {
-    result = result.replace(/, /g, ',');
+    result = result.replace(regEx(/, /g), ',');
   }
 
   if (!satisfies(newVersion, result)) {
@@ -163,8 +164,8 @@ export function isLessThanRange(input: string, range: string): boolean {
       .split(',')
       .map((x) =>
         x
-          .replace(/\s*/g, '')
-          .split(/(~=|==|!=|<=|>=|<|>|===)/)
+          .replace(regEx(/\s*/g), '') // TODO #12071
+          .split(regEx(/(~=|==|!=|<=|>=|<|>|===)/)) // TODO #12071
           .slice(1)
       )
       .map(([op, version]) => {
diff --git a/lib/versioning/rez/index.ts b/lib/versioning/rez/index.ts
index 6b848afcfbe64525b546e2d910cdff188386c046..f027ed5254212dc15c6ab03d1d7a6d507a58a1d5 100644
--- a/lib/versioning/rez/index.ts
+++ b/lib/versioning/rez/index.ts
@@ -1,3 +1,4 @@
+import { regEx } from '../../util/regex';
 import { api as npm } from '../npm';
 import { api as pep440 } from '../pep440';
 import type { NewValueConfig, VersioningApi } from '../types';
@@ -146,12 +147,8 @@ function getNewValue({
     const lowerAscVersionCurrent = match.groups.range_lower_asc_version;
     const upperAscVersionCurrent = match.groups.range_upper_asc_version;
     const [lowerBoundAscPep440, upperBoundAscPep440] = pep440Value.split(', ');
-    const lowerAscVersionNew = new RegExp(versionGroup).exec(
-      lowerBoundAscPep440
-    )[0];
-    const upperAscVersionNew = new RegExp(versionGroup).exec(
-      upperBoundAscPep440
-    )[0];
+    const lowerAscVersionNew = regEx(versionGroup).exec(lowerBoundAscPep440)[0];
+    const upperAscVersionNew = regEx(versionGroup).exec(upperBoundAscPep440)[0];
     const lowerBoundAscNew = lowerBoundAscCurrent.replace(
       lowerAscVersionCurrent,
       lowerAscVersionNew
@@ -175,12 +172,10 @@ function getNewValue({
     const [lowerBoundDescPep440, upperBoundDescPep440] =
       pep440Value.split(', ');
 
-    const upperDescVersionNew = new RegExp(versionGroup).exec(
-      upperBoundDescPep440
-    )[0];
-    const lowerDescVersionNew = new RegExp(versionGroup).exec(
-      lowerBoundDescPep440
-    )[0];
+    const upperDescVersionNew =
+      regEx(versionGroup).exec(upperBoundDescPep440)[0];
+    const lowerDescVersionNew =
+      regEx(versionGroup).exec(lowerBoundDescPep440)[0];
     const upperBoundDescNew = upperBoundDescCurrent.replace(
       upperDescVersionCurrent,
       upperDescVersionNew
diff --git a/lib/versioning/rez/pattern.ts b/lib/versioning/rez/pattern.ts
index 9132efa141fdeedfc8bb66aa85c70c46630cc4dd..d2d7c98e46d39acf9fa29c7bb6dd89be3966cff2 100644
--- a/lib/versioning/rez/pattern.ts
+++ b/lib/versioning/rez/pattern.ts
@@ -59,31 +59,34 @@
 // - Replace {version_group} -> ${versionGroup}
 // - Replace (?P<...>) -> (?<...>)
 // - Replace ?(...) -> \k<...>
+
+import { regEx } from '../../util/regex';
+
 // - Replace single \ -> double \
 export const versionGroup = '([0-9a-zA-Z_]+(?:[.-][0-9a-zA-Z_]+)*)';
-export const matchVersion = new RegExp(
+export const matchVersion = regEx(
   `^(?<version>${versionGroup})$`
 ); /* Match a version number (e.g. 1.0.0) */
-export const exactVersion = new RegExp(
+export const exactVersion = regEx(
   `^(?<exact_version>==(?<exact_version_group>${versionGroup})?)$`
 ); /* Match an exact version number (e.g. ==1.0.0) */
 // inclusiveBound is called inclusive but behaviour in rez is this:
 // package-1..3 will match versions 1.2.3, 2.3.4, but not 3.0.0 or above
-export const inclusiveBound = new RegExp(
+export const inclusiveBound = regEx(
   `^(?<inclusive_bound>(?<inclusive_lower_version>${versionGroup})?\\.\\.(?<inclusive_upper_version>${versionGroup})?)$`
 ); /* Match an inclusive bound (e.g. 1.0.0..2.0.0) */
 // Add ? after |\\+) in order to match >=1.15
-export const lowerBound = new RegExp(
+export const lowerBound = new RegExp( // TODO #12070
   `^(?<lower_bound>(?<lower_bound_prefix>>|>=)?(?<lower_version>${versionGroup})?(\\k<lower_bound_prefix>|\\+)?)$`
 ); /* Match a lower bound (e.g. 1.0.0+) */
-export const upperBound = new RegExp(
+export const upperBound = new RegExp( // TODO #12070
   `^(?<upper_bound>(?<upper_bound_prefix><(?=${versionGroup})|<=)?(?<upper_version>${versionGroup})?)$`
 ); /* Match an upper bound (e.g. <=1.0.0) */
 // Add ,? to match >=7,<9 (otherwise it just matches >=7<9)
-export const ascendingRange = new RegExp(
+export const ascendingRange = new RegExp( // TODO #12070
   `^(?<range_asc>(?<range_lower_asc>(?<range_lower_asc_prefix>>|>=)?(?<range_lower_asc_version>${versionGroup})?(\\k<range_lower_asc_prefix>|\\+)?),?(?<range_upper_asc>(\\k<range_lower_asc_version>,?|)(?<range_upper_asc_prefix><(?=${versionGroup})|<=)(?<range_upper_asc_version>${versionGroup})?))$`
 ); /* Match a range in ascending order (e.g. 1.0.0+<2.0.0) */
 // Add , to match <9,>=7 (otherwise it just matches <9>=7)
-export const descendingRange = new RegExp(
+export const descendingRange = new RegExp( // TODO #12070
   `^(?<range_desc>(?<range_upper_desc>(?<range_upper_desc_prefix><|<=)?(?<range_upper_desc_version>${versionGroup})?(\\k<range_upper_desc_prefix>|\\+)?),(?<range_lower_desc>(\\k<range_upper_desc_version>,|)(?<range_lower_desc_prefix><(?=${versionGroup})|>=?)(?<range_lower_desc_version>${versionGroup})?))$`
 ); /* Match a range in descending order (e.g. <=2.0.0,1.0.0+) */
diff --git a/lib/versioning/rez/transform.ts b/lib/versioning/rez/transform.ts
index b5685560821795200e78c246996431d012aa7746..9b1cb5556da5fbc370581880d5b345e161903cee 100644
--- a/lib/versioning/rez/transform.ts
+++ b/lib/versioning/rez/transform.ts
@@ -1,3 +1,4 @@
+import { regEx } from '../../util/regex';
 import {
   ascendingRange,
   descendingRange,
@@ -18,7 +19,7 @@ function getVersionParts(input: string): [string, string] {
 }
 
 export function padZeroes(input: string): string {
-  if (/[~^*]/.test(input)) {
+  if (regEx(/[~^*]/).test(input)) {
     // ignore ranges
     return input;
   }
@@ -47,7 +48,7 @@ export function rez2npm(input: string): string {
     return input.replace('==', '=');
   }
   if (inclusiveBound.test(input)) {
-    return '>=' + input.replace(/\.\./g, ' <');
+    return '>=' + input.replace(regEx(/\.\./g), ' <');
   }
   if (lowerBound.test(input)) {
     return plus2npm(input);
@@ -78,7 +79,7 @@ export function rez2pep440(input: string): string {
     return input;
   }
   if (inclusiveBound.test(input)) {
-    return '>=' + input.replace(/\.\./g, ', <');
+    return '>=' + input.replace(regEx(/\.\./g), ', <');
   }
   if (lowerBound.test(input)) {
     return plus2npm(input);
@@ -104,7 +105,7 @@ export function rez2pep440(input: string): string {
 export function pep4402rezInclusiveBound(input: string): string {
   return input
     .split(',')
-    .map((v) => v.trim().replace(/[<>=]/g, ''))
+    .map((v) => v.trim().replace(regEx(/[<>=]/g), '')) // TODO #12071
     .join('..');
 }
 
diff --git a/lib/versioning/ruby/index.ts b/lib/versioning/ruby/index.ts
index e682c9fe4196bf02f160ab106bd0d01eeac9e69e..929e267ae90ff062bdcbd49b2bffc3b1e2aa002d 100644
--- a/lib/versioning/ruby/index.ts
+++ b/lib/versioning/ruby/index.ts
@@ -7,6 +7,7 @@ import {
   valid,
 } from '@renovatebot/ruby-semver';
 import { logger } from '../../logger';
+import { regEx } from '../../util/regex';
 import type { NewValueConfig, VersioningApi } from '../types';
 import { isSingleOperator, isValidOperator } from './operator';
 import { ltr, parse as parseRange } from './range';
@@ -25,7 +26,7 @@ export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];
 
 function vtrim<T = unknown>(version: T): string | T {
   if (typeof version === 'string') {
-    return version.replace(/^v/, '').replace(/('|")/g, '');
+    return version.replace(regEx(/^v/), '').replace(regEx(/('|")/g), '');
   }
   return version;
 }
@@ -87,7 +88,7 @@ const getNewValue = ({
   let newValue = null;
   if (isVersion(currentValue)) {
     newValue = currentValue.startsWith('v') ? 'v' + newVersion : newVersion;
-  } else if (currentValue.replace(/^=\s*/, '') === currentVersion) {
+  } else if (currentValue.replace(regEx(/^=\s*/), '') === currentVersion) {
     newValue = currentValue.replace(currentVersion, newVersion);
   } else {
     switch (rangeStrategy) {
@@ -122,15 +123,17 @@ const getNewValue = ({
         logger.warn(`Unsupported strategy ${rangeStrategy}`);
     }
   }
-  if (/^('|")/.exec(currentValue)) {
+  if (regEx(/^('|")/).exec(currentValue)) {
     const delimiter = currentValue[0];
     return newValue
       .split(',')
-      .map((element) =>
-        element.replace(/^(?<whitespace>\s*)/, `$<whitespace>${delimiter}`)
+      .map(
+        (element) =>
+          element.replace(/^(?<whitespace>\s*)/, `$<whitespace>${delimiter}`) // TODO #12071 #12070
       )
-      .map((element) =>
-        element.replace(/(?<whitespace>\s*)$/, `${delimiter}$<whitespace>`)
+      .map(
+        (element) =>
+          element.replace(/(?<whitespace>\s*)$/, `${delimiter}$<whitespace>`) // TODO #12071 #12070
       )
       .join(',');
   }
diff --git a/lib/versioning/ruby/range.ts b/lib/versioning/ruby/range.ts
index 3677f81d79664c7105b72af1f37eb98b37512245..3304337f1142cc890887cebbe6c7b7a952bf57f6 100644
--- a/lib/versioning/ruby/range.ts
+++ b/lib/versioning/ruby/range.ts
@@ -1,6 +1,7 @@
 import { parse as _parse } from '@renovatebot/ruby-semver/dist/ruby/requirement';
 import { Version, create } from '@renovatebot/ruby-semver/dist/ruby/version';
 import { logger } from '../../logger';
+import { regEx } from '../../util/regex';
 import { EQUAL, GT, GTE, LT, LTE, NOT_EQUAL, PGTE } from './operator';
 
 export interface Range {
@@ -10,8 +11,9 @@ export interface Range {
 }
 
 const parse = (range: string): Range => {
-  const regExp =
-    /^(?<operator>[^\d\s]+)?(?<delimiter>\s*)(?<version>[0-9a-zA-Z-.]+)$/;
+  const regExp = regEx(
+    /^(?<operator>[^\d\s]+)?(?<delimiter>\s*)(?<version>[0-9a-zA-Z-.]+)$/
+  );
 
   const value = (range || '').trim();
 
diff --git a/lib/versioning/ruby/version.ts b/lib/versioning/ruby/version.ts
index f6f7b44a934893bf66e2d1fe368a20cc556c645a..6ae868859b066ca15e51dee9ca671e8eb576e1e7 100644
--- a/lib/versioning/ruby/version.ts
+++ b/lib/versioning/ruby/version.ts
@@ -3,6 +3,7 @@ import {
   SegmentElement,
   create,
 } from '@renovatebot/ruby-semver/dist/ruby/version';
+import { regEx } from '../../util/regex';
 
 interface RubyVersion {
   major: number;
@@ -71,7 +72,7 @@ const increment = (from: string, to: string): string => {
     return incrementLastSegment(from);
   }
 
-  const isStable = (x: string): boolean => /^[0-9.-/]+$/.test(x);
+  const isStable = (x: string): boolean => regEx(/^[0-9.-/]+$/).test(x);
   if (major(from) !== major(adapted)) {
     nextVersion = [incrementMajor(maj, min, ptch, pre || []), 0, 0].join('.');
   } else if (minor(from) !== minor(adapted)) {
diff --git a/lib/versioning/swift/index.ts b/lib/versioning/swift/index.ts
index c34127c895bd0e4f688ad26c983c9e5c358fa1cd..fa7ba22a6bee6b8d834d7122c483920a72100842 100644
--- a/lib/versioning/swift/index.ts
+++ b/lib/versioning/swift/index.ts
@@ -1,5 +1,6 @@
 import semver from 'semver';
 import stable from 'semver-stable';
+import { regEx } from '../../util/regex';
 import type { VersioningApi } from '../types';
 import { getNewValue, toSemverRange } from './range';
 
@@ -31,12 +32,12 @@ export const isValid = (input: string): boolean =>
 export const isVersion = (input: string): boolean => !!valid(input);
 const getSatisfyingVersion = (versions: string[], range: string): string =>
   maxSatisfying(
-    versions.map((v) => v.replace(/^v/, '')),
+    versions.map((v) => v.replace(regEx(/^v/), '')), // TODO #12071
     toSemverRange(range)
   );
 const minSatisfyingVersion = (versions: string[], range: string): string =>
   minSatisfying(
-    versions.map((v) => v.replace(/^v/, '')),
+    versions.map((v) => v.replace(regEx(/^v/), '')), // TODO #12071 #12070
     toSemverRange(range)
   );
 const isLessThanRange = (version: string, range: string): boolean =>
diff --git a/lib/versioning/swift/range.ts b/lib/versioning/swift/range.ts
index 52f6321d08e7d83b1f9b4d51a67359cb800dbec7..18ce3d20e0638b5f871dd0dbaa7397bc1e5f5a94 100644
--- a/lib/versioning/swift/range.ts
+++ b/lib/versioning/swift/range.ts
@@ -1,10 +1,11 @@
 import semver from 'semver';
+import { regEx } from '../../util/regex';
 import type { NewValueConfig } from '../types';
 
-const fromParam = /^\s*from\s*:\s*"([^"]+)"\s*$/;
-const fromRange = /^\s*"([^"]+)"\s*\.\.\.\s*$/;
-const binaryRange = /^\s*"([^"]+)"\s*(\.\.[.<])\s*"([^"]+)"\s*$/;
-const toRange = /^\s*(\.\.[.<])\s*"([^"]+)"\s*$/;
+const fromParam = regEx(/^\s*from\s*:\s*"([^"]+)"\s*$/);
+const fromRange = regEx(/^\s*"([^"]+)"\s*\.\.\.\s*$/);
+const binaryRange = regEx(/^\s*"([^"]+)"\s*(\.\.[.<])\s*"([^"]+)"\s*$/);
+const toRange = regEx(/^\s*(\.\.[.<])\s*"([^"]+)"\s*$/);
 
 function toSemverRange(range: string): string {
   if (fromParam.test(range)) {
@@ -40,7 +41,7 @@ function getNewValue({
   newVersion,
 }: NewValueConfig): string {
   if (fromParam.test(currentValue)) {
-    return currentValue.replace(/".*?"/, `"${newVersion}"`);
+    return currentValue.replace(regEx(/".*?"/), `"${newVersion}"`);
   }
   if (fromRange.test(currentValue)) {
     const [, version] = fromRange.exec(currentValue);
diff --git a/lib/versioning/ubuntu/index.ts b/lib/versioning/ubuntu/index.ts
index 9e7d2921b0feaecb3e9dfad314fcc0c3b614020d..0e4d3a22dd7a6c796d2e8db7bc1a9e344401721a 100644
--- a/lib/versioning/ubuntu/index.ts
+++ b/lib/versioning/ubuntu/index.ts
@@ -1,3 +1,4 @@
+import { regEx } from '../../util/regex';
 import type { NewValueConfig, VersioningApi } from '../types';
 
 export const id = 'ubuntu';
@@ -10,7 +11,7 @@ export const supportsRanges = false;
 function isValid(input: string): string | boolean | null {
   return (
     typeof input === 'string' &&
-    /^(0[4-5]|[6-9]|[1-9][0-9])\.[0-9][0-9](\.[0-9]{1,2})?$/.test(input)
+    regEx(/^(0[4-5]|[6-9]|[1-9][0-9])\.[0-9][0-9](\.[0-9]{1,2})?$/).test(input)
   );
 }
 
@@ -33,7 +34,7 @@ function isStable(version: string): boolean {
   if (!isValid(version)) {
     return false;
   }
-  return /^\d?[02468]\.04/.test(version);
+  return regEx(/^\d?[02468]\.04/).test(version);
 }
 
 // digestion of version
diff --git a/lib/workers/branch/index.ts b/lib/workers/branch/index.ts
index da893445930b4a6d3380b4028ca6dd21051ec254..00d27a9ac4fc8364e4e54aa89acfb51dda0a7123 100644
--- a/lib/workers/branch/index.ts
+++ b/lib/workers/branch/index.ts
@@ -32,6 +32,7 @@ import {
   isActiveConfidenceLevel,
   satisfiesConfidenceLevel,
 } from '../../util/merge-confidence';
+import { regEx } from '../../util/regex';
 import { Limit, isLimitReached } from '../global/limits';
 import { ensurePr, getPlatformPrOptions } from '../pr';
 import { checkAutoMerge } from '../pr/automerge';
@@ -57,7 +58,7 @@ function rebaseCheck(config: RenovateConfig, branchPr: Pr): boolean {
   return titleRebase || labelRebase || prRebaseChecked;
 }
 
-const rebasingRegex = /\*\*Rebasing\*\*: .*/;
+const rebasingRegex = regEx(/\*\*Rebasing\*\*: .*/);
 
 async function deleteBranchSilently(branchName: string): Promise<void> {
   try {
diff --git a/lib/workers/branch/schedule.ts b/lib/workers/branch/schedule.ts
index 443510053c43ef4faca0cec13bbe28c66fcf28bc..71696d72ff125560e987e99a0ba03e30a5d4215a 100644
--- a/lib/workers/branch/schedule.ts
+++ b/lib/workers/branch/schedule.ts
@@ -3,6 +3,7 @@ import is from '@sindresorhus/is';
 import { DateTime } from 'luxon';
 import type { RenovateConfig } from '../../config/types';
 import { logger } from '../../logger';
+import { regEx } from '../../util/regex';
 
 const scheduleMappings: Record<string, string> = {
   'every month': 'before 3am on the first day of the month',
@@ -10,7 +11,7 @@ const scheduleMappings: Record<string, string> = {
 };
 
 function fixShortHours(input: string): string {
-  return input.replace(/( \d?\d)((a|p)m)/g, '$1:00$2');
+  return input.replace(regEx(/( \d?\d)((a|p)m)/g), '$1:00$2');
 }
 
 export function hasValidTimezone(
diff --git a/lib/workers/global/config/parse/cli.ts b/lib/workers/global/config/parse/cli.ts
index 1e224a736ed17e48a405911a7d411537460d470f..a318e9944151c1ff3af5f9360101832de767106b 100644
--- a/lib/workers/global/config/parse/cli.ts
+++ b/lib/workers/global/config/parse/cli.ts
@@ -2,12 +2,13 @@ import { Command } from 'commander';
 import { version } from '../../../../../package.json';
 import { getOptions } from '../../../../config/options';
 import type { AllConfig, RenovateOptions } from '../../../../config/types';
+import { regEx } from '../../../../util/regex';
 
 export function getCliName(option: Partial<RenovateOptions>): string {
   if (option.cli === false) {
     return '';
   }
-  const nameWithHyphens = option.name.replace(/([A-Z])/g, '-$1');
+  const nameWithHyphens = option.name.replace(regEx(/([A-Z])/g), '-$1');
   return `--${nameWithHyphens.toLowerCase()}`;
 }
 
diff --git a/lib/workers/global/config/parse/env.ts b/lib/workers/global/config/parse/env.ts
index 3571174e4f2082842384e99564ad7accb3fe6b2c..51353ef48bca8546eeed25eb56d73de680a05aae 100644
--- a/lib/workers/global/config/parse/env.ts
+++ b/lib/workers/global/config/parse/env.ts
@@ -1,5 +1,4 @@
 import is from '@sindresorhus/is';
-
 import { getOptions } from '../../../../config/options';
 import type { AllConfig, RenovateOptions } from '../../../../config/types';
 import { PlatformId } from '../../../../constants';
diff --git a/lib/workers/pr/body/changelogs.ts b/lib/workers/pr/body/changelogs.ts
index a0e62d6910b346b315c9c58711629adee7fcbb28..73aa37c51d5f776a64dfe5bdc1af9eaf011e91ed 100644
--- a/lib/workers/pr/body/changelogs.ts
+++ b/lib/workers/pr/body/changelogs.ts
@@ -1,5 +1,6 @@
 import { unemojify } from '../../../util/emoji';
 import { sanitizeMarkdown } from '../../../util/markdown';
+import { regEx } from '../../../util/regex';
 import * as template from '../../../util/template';
 import type { BranchConfig } from '../../types';
 import releaseNotesHbs from '../changelog/hbs-template';
@@ -12,7 +13,7 @@ export function getChangelogs(config: BranchConfig): string {
   }
   releaseNotes +=
     '\n\n---\n\n' + template.compile(releaseNotesHbs, config, false) + '\n\n';
-  releaseNotes = releaseNotes.replace(/### \[`vv/g, '### [`v');
+  releaseNotes = releaseNotes.replace(regEx(/### \[`vv/g), '### [`v');
   releaseNotes = sanitizeMarkdown(releaseNotes);
   releaseNotes = unemojify(releaseNotes);
 
diff --git a/lib/workers/pr/body/index.ts b/lib/workers/pr/body/index.ts
index 275476d77e9905a512fb3144456700436e73cf10..6aa5a015af8b6452bfdf29a01d8ae72e7c0a5f2f 100644
--- a/lib/workers/pr/body/index.ts
+++ b/lib/workers/pr/body/index.ts
@@ -1,4 +1,5 @@
 import { platform } from '../../../platform';
+import { regEx } from '../../../util/regex';
 import * as template from '../../../util/template';
 import type { BranchConfig } from '../../types';
 import { getChangelogs } from './changelogs';
@@ -43,7 +44,7 @@ function massageUpdateMetadata(config: BranchConfig): void {
       let fullUrl = sourceUrl;
       if (sourceDirectory) {
         fullUrl =
-          sourceUrl.replace(/\/?$/, '/') +
+          sourceUrl.replace(regEx(/\/?$/), '/') +
           'tree/HEAD/' +
           sourceDirectory.replace('^/?/', '');
       }
@@ -71,7 +72,7 @@ export async function getPrBody(config: BranchConfig): Promise<string> {
   const prBodyTemplate = config.prBodyTemplate;
   let prBody = template.compile(prBodyTemplate, content, false);
   prBody = prBody.trim();
-  prBody = prBody.replace(/\n\n\n+/g, '\n\n');
+  prBody = prBody.replace(regEx(/\n\n\n+/g), '\n\n');
   prBody = platform.massageMarkdown(prBody);
   return prBody;
 }
diff --git a/lib/workers/pr/body/updates-table.ts b/lib/workers/pr/body/updates-table.ts
index 06607b31c38dc90a3f9cd9edf10c4b4909399522..173fe873c94748061fe3bc0ebc6d87e52ba9a5de 100644
--- a/lib/workers/pr/body/updates-table.ts
+++ b/lib/workers/pr/body/updates-table.ts
@@ -1,4 +1,5 @@
 import { logger } from '../../../logger';
+import { regEx } from '../../../util/regex';
 import * as template from '../../../util/template';
 import type { BranchConfig } from '../../types';
 
@@ -43,7 +44,9 @@ export function getPrUpdatesTable(config: BranchConfig): string {
       try {
         // istanbul ignore else
         if (value) {
-          res[header] = template.compile(value, upgrade).replace(/^``$/, '');
+          res[header] = template
+            .compile(value, upgrade)
+            .replace(regEx(/^``$/), ''); // TODO #12071
         } else {
           res[header] = '';
         }
@@ -62,7 +65,9 @@ export function getPrUpdatesTable(config: BranchConfig): string {
     let val = '|';
     for (const column of tableColumns) {
       const content = row[column]
-        ? row[column].replace(/^@/, '@&#8203;').replace(/\|/g, '\\|')
+        ? row[column]
+            .replace(regEx(/^@/), '@&#8203;')
+            .replace(regEx(/\|/g), '\\|') // TODO #12071
         : '';
       val += ` ${content} |`;
     }
diff --git a/lib/workers/pr/changelog/gitlab/index.ts b/lib/workers/pr/changelog/gitlab/index.ts
index 282e2f5c218b1fe80a2e6bb52b43fcb20fb7ccad..3364cffb9e1a44b54afced7f592e0844cf4bbac0 100644
--- a/lib/workers/pr/changelog/gitlab/index.ts
+++ b/lib/workers/pr/changelog/gitlab/index.ts
@@ -4,13 +4,14 @@ import type { GitlabTag } from '../../../../datasource/gitlab-tags/types';
 import { logger } from '../../../../logger';
 import type { GitlabTreeNode } from '../../../../types/platform/gitlab';
 import { GitlabHttp } from '../../../../util/http/gitlab';
+import { regEx } from '../../../../util/regex';
 import { ensureTrailingSlash } from '../../../../util/url';
 import type { ChangeLogFile, ChangeLogNotes } from '../types';
 
 const http = new GitlabHttp();
 
 function getRepoId(repository: string): string {
-  return repository.replace(/\//g, '%2f');
+  return repository.replace(regEx(/\//g), '%2f');
 }
 
 export async function getTags(
diff --git a/lib/workers/pr/changelog/release-notes.ts b/lib/workers/pr/changelog/release-notes.ts
index 43067c97094d56de29623305858325e067b57607..f365fde6e9a468d4a07dbf37774be6cb0a145621 100644
--- a/lib/workers/pr/changelog/release-notes.ts
+++ b/lib/workers/pr/changelog/release-notes.ts
@@ -6,6 +6,7 @@ import { logger } from '../../../logger';
 import * as memCache from '../../../util/cache/memory';
 import * as packageCache from '../../../util/cache/package';
 import { linkify } from '../../../util/markdown';
+import { regEx } from '../../../util/regex';
 import * as github from './github';
 import * as gitlab from './gitlab';
 import type {
@@ -67,25 +68,23 @@ export function massageBody(
 ): string {
   let body = input || '';
   // Convert line returns
-  body = body.replace(/\r\n/g, '\n');
+  body = body.replace(regEx(/\r\n/g), '\n'); // TODO #12071
   // semantic-release cleanup
-  body = body.replace(/^<a name="[^"]*"><\/a>\n/, '');
+  body = body.replace(regEx(/^<a name="[^"]*"><\/a>\n/), ''); // TODO #12071
   body = body.replace(
-    new RegExp(
-      `^##? \\[[^\\]]*\\]\\(${baseUrl}[^/]*\\/[^/]*\\/compare\\/.*?\\n`
-    ),
+    regEx(`^##? \\[[^\\]]*\\]\\(${baseUrl}[^/]*\\/[^/]*\\/compare\\/.*?\\n`),
     ''
-  );
+  ); // TODO #12071
   // Clean-up unnecessary commits link
   body = `\n${body}\n`.replace(
-    new RegExp(`\\n${baseUrl}[^/]+\\/[^/]+\\/compare\\/[^\\n]+(\\n|$)`),
+    regEx(`\\n${baseUrl}[^/]+\\/[^/]+\\/compare\\/[^\\n]+(\\n|$)`),
     '\n'
-  );
+  ); // TODO #12071
   // Reduce headings size
   body = body
-    .replace(/\n\s*####? /g, '\n##### ')
-    .replace(/\n\s*## /g, '\n#### ')
-    .replace(/\n\s*# /g, '\n### ');
+    .replace(regEx(/\n\s*####? /g), '\n##### ') // TODO #12071
+    .replace(regEx(/\n\s*## /g), '\n#### ') // TODO #12071
+    .replace(regEx(/\n\s*# /g), '\n### '); // TODO #12071
   // Trim whitespace
   return body.trim();
 }
@@ -236,7 +235,7 @@ export async function getReleaseNotesMd(
   }
   const { changelogFile } = changelog;
   const changelogMd = changelog.changelogMd.replace(
-    /\n\s*<a name="[^"]*">.*?<\/a>\n/g,
+    regEx(/\n\s*<a name="[^"]*">.*?<\/a>\n/g),
     '\n'
   );
   for (const level of [1, 2, 3, 4, 5, 6, 7]) {
@@ -245,19 +244,22 @@ export async function getReleaseNotesMd(
       for (const section of changelogParsed) {
         try {
           // replace brackets and parenthesis with space
-          const deParenthesizedSection = section.replace(/[[\]()]/g, ' ');
+          const deParenthesizedSection = section.replace(
+            regEx(/[[\]()]/g),
+            ' '
+          ); // TODO #12071
           const [heading] = deParenthesizedSection.split('\n');
           const title = heading
-            .replace(/^\s*#*\s*/, '')
+            .replace(regEx(/^\s*#*\s*/), '') // TODO #12071
             .split(' ')
             .filter(Boolean);
-          let body = section.replace(/.*?\n(-{3,}\n)?/, '').trim();
+          let body = section.replace(regEx(/.*?\n(-{3,}\n)?/), '').trim(); // TODO #12071
           for (const word of title) {
             if (word.includes(version) && !isUrl(word)) {
               logger.trace({ body }, 'Found release notes for v' + version);
               // TODO: fix url
               let url = `${baseUrl}${repository}/blob/master/${changelogFile}#`;
-              url += title.join('-').replace(/[^A-Za-z0-9-]/g, '');
+              url += title.join('-').replace(regEx(/[^A-Za-z0-9-]/g), ''); // TODO #12071
               body = massageBody(body, baseUrl);
               if (body?.length) {
                 try {
diff --git a/lib/workers/pr/changelog/source-github.ts b/lib/workers/pr/changelog/source-github.ts
index 720ef8c4d8b0c18b0f78146fcdb23744820a5c84..963103752b0797f46dfbf82fad0010f48faeb7d8 100644
--- a/lib/workers/pr/changelog/source-github.ts
+++ b/lib/workers/pr/changelog/source-github.ts
@@ -5,6 +5,7 @@ import { logger } from '../../../logger';
 import * as memCache from '../../../util/cache/memory';
 import * as packageCache from '../../../util/cache/package';
 import * as hostRules from '../../../util/host-rules';
+import { regEx } from '../../../util/regex';
 import * as allVersioning from '../../../versioning';
 import type { BranchUpgradeConfig } from '../../types';
 import { getTags } from './github';
@@ -70,8 +71,8 @@ export async function getChangeLogJSON({
     : baseUrl + 'api/v3/';
   const repository = pathname
     .slice(1)
-    .replace(/\/$/, '')
-    .replace(/\.git$/, '');
+    .replace(regEx(/\/$/), '')
+    .replace(regEx(/\.git$/), '');
   if (repository.split('/').length !== 2) {
     logger.debug({ sourceUrl }, 'Invalid github URL found');
     return null;
@@ -96,7 +97,7 @@ export async function getChangeLogJSON({
     if (!tags) {
       tags = await getCachedTags(apiBaseUrl, repository);
     }
-    const regex = new RegExp(`(?:${depName}|release)[@-]`);
+    const regex = regEx(`(?:${depName}|release)[@-]`);
     const tagName = tags
       .filter((tag) => version.isVersion(tag.replace(regex, '')))
       .find((tag) => version.equals(tag.replace(regex, ''), release.version));
diff --git a/lib/workers/pr/changelog/source-gitlab.ts b/lib/workers/pr/changelog/source-gitlab.ts
index 9eb713d13b03f1050976147c8e9cf6984a857a4c..f7a0505142b6f7346b4458da56beab9b65481c0b 100644
--- a/lib/workers/pr/changelog/source-gitlab.ts
+++ b/lib/workers/pr/changelog/source-gitlab.ts
@@ -46,8 +46,8 @@ export async function getChangeLogJSON({
   const apiBaseUrl = baseUrl.concat('api/v4/');
   const repository = pathname
     .slice(1)
-    .replace(/\/$/, '')
-    .replace(/\.git$/, '');
+    .replace(regEx(/\/$/), '')
+    .replace(regEx(/\.git$/), '');
   if (repository.split('/').length < 2) {
     logger.info({ sourceUrl }, 'Invalid gitlab URL found');
     return null;
diff --git a/lib/workers/pr/code-owners.ts b/lib/workers/pr/code-owners.ts
index 731c9e4e7b504e8aed022830b790e2cbe0e55a67..22f7681adb6a0a9afc5e4aaa114494814a481500 100644
--- a/lib/workers/pr/code-owners.ts
+++ b/lib/workers/pr/code-owners.ts
@@ -3,6 +3,7 @@ import { logger } from '../../logger';
 import { Pr } from '../../platform';
 import { readLocalFile } from '../../util/fs';
 import { getBranchFiles } from '../../util/git';
+import { regEx } from '../../util/regex';
 
 export async function codeOwnersForPr(pr: Pr): Promise<string[]> {
   logger.debug('Searching for CODEOWNERS file');
@@ -26,7 +27,7 @@ export async function codeOwnersForPr(pr: Pr): Promise<string[]> {
       .map((line) => line.trim())
       .filter((line) => line && !line.startsWith('#'))
       .map((line) => {
-        const [pattern, ...usernames] = line.split(/\s+/);
+        const [pattern, ...usernames] = line.split(regEx(/\s+/)); // TODO #12071
         return {
           usernames,
           match: (path: string) => {
diff --git a/lib/workers/pr/index.ts b/lib/workers/pr/index.ts
index fb8bda96179a52eee62da0218aea5b603088b1dc..e84858b73fa86b75670b5ba5f116c5696cd46378 100644
--- a/lib/workers/pr/index.ts
+++ b/lib/workers/pr/index.ts
@@ -12,6 +12,7 @@ import { ExternalHostError } from '../../types/errors/external-host-error';
 import { sampleSize } from '../../util';
 import { stripEmojis } from '../../util/emoji';
 import { deleteBranch, getBranchLastCommitTime } from '../../util/git';
+import { regEx } from '../../util/regex';
 import * as template from '../../util/template';
 import { resolveBranchStatus } from '../branch/status-checks';
 import { Limit, incLimitedValue, isLimitReached } from '../global/limits';
@@ -21,7 +22,7 @@ import { ChangeLogError } from './changelog/types';
 import { codeOwnersForPr } from './code-owners';
 
 function noWhitespaceOrHeadings(input: string): string {
-  return input.replace(/\r?\n|\r|\s|#/g, '');
+  return input.replace(regEx(/\r?\n|\r|\s|#/g), '');
 }
 
 function noLeadingAtSymbol(input: string): string {
diff --git a/lib/workers/repository/dependency-dashboard.ts b/lib/workers/repository/dependency-dashboard.ts
index 3366d963ce73d149e6dc5078b5bab1992191b46b..6dc1dded4dcd2f2242cbd2a403e17da7f7c58d22 100644
--- a/lib/workers/repository/dependency-dashboard.ts
+++ b/lib/workers/repository/dependency-dashboard.ts
@@ -4,6 +4,7 @@ import { getGlobalConfig } from '../../config/global';
 import type { RenovateConfig } from '../../config/types';
 import { getProblems, logger } from '../../logger';
 import { platform } from '../../platform';
+import { regEx } from '../../util/regex';
 import { BranchConfig, BranchResult } from '../types';
 
 interface DependencyDashboard {
@@ -13,10 +14,10 @@ interface DependencyDashboard {
 
 function parseDashboardIssue(issueBody: string): DependencyDashboard {
   const checkMatch = ' - \\[x\\] <!-- ([a-zA-Z]+)-branch=([^\\s]+) -->';
-  const checked = issueBody.match(new RegExp(checkMatch, 'g'));
+  const checked = issueBody.match(regEx(checkMatch, 'g'));
   const dependencyDashboardChecks: Record<string, string> = {};
   if (checked?.length) {
-    const re = new RegExp(checkMatch);
+    const re = regEx(checkMatch);
     checked.forEach((check) => {
       const [, type, branchName] = re.exec(check);
       dependencyDashboardChecks[branchName] = type;
diff --git a/lib/workers/repository/error-config.ts b/lib/workers/repository/error-config.ts
index 752c46edc2b9cfe4ed1487e67a908defd0549d98..5b10967bac489b9aea043305b24d7591ec6928fd 100644
--- a/lib/workers/repository/error-config.ts
+++ b/lib/workers/repository/error-config.ts
@@ -3,6 +3,7 @@ import type { RenovateConfig } from '../../config/types';
 import { logger } from '../../logger';
 import { platform } from '../../platform';
 import { PrState } from '../../types';
+import { regEx } from '../../util/regex';
 
 export async function raiseConfigWarningIssue(
   config: RenovateConfig,
@@ -15,7 +16,10 @@ export async function raiseConfigWarningIssue(
   }
   body += `Error type: ${error.validationError}\n`;
   if (error.validationMessage) {
-    body += `Message: \`${error.validationMessage.replace(/`/g, "'")}\`\n`;
+    body += `Message: \`${error.validationMessage.replace(
+      regEx(/`/g),
+      "'"
+    )}\`\n`;
   }
   const pr = await platform.getBranchPr(config.onboardingBranch);
   if (pr?.state === PrState.Open) {
diff --git a/lib/workers/repository/init/vulnerability.ts b/lib/workers/repository/init/vulnerability.ts
index 4bd4a5e87198d122bbaa914391e64f2875a2ebf2..85cbb3744d72ae2f3c3b8147ec7469b0d26701dc 100644
--- a/lib/workers/repository/init/vulnerability.ts
+++ b/lib/workers/repository/init/vulnerability.ts
@@ -9,6 +9,7 @@ import { logger } from '../../../logger';
 import { platform } from '../../../platform';
 import { SecurityAdvisory } from '../../../types';
 import { sanitizeMarkdown } from '../../../util/markdown';
+import { regEx } from '../../../util/regex';
 import * as allVersioning from '../../../versioning';
 import * as mavenVersioning from '../../../versioning/maven';
 import * as npmVersioning from '../../../versioning/npm';
@@ -110,7 +111,10 @@ export async function detectVulnerabilityAlerts(
         }
       }
       if (datasource === PypiDatasource.id) {
-        vulnerableRequirements = vulnerableRequirements.replace(/^= /, '== ');
+        vulnerableRequirements = vulnerableRequirements.replace(
+          regEx(/^= /),
+          '== '
+        ); // TODO #12071
       }
       combinedAlerts[fileName] ||= {};
       combinedAlerts[fileName][datasource] ||= {};
diff --git a/lib/workers/repository/onboarding/pr/pr-list.ts b/lib/workers/repository/onboarding/pr/pr-list.ts
index f5adce06102e47be6ec817a2d04be26d624516b9..b381c9f485966a77faf3ab0d99f6cddf0de6eb6d 100644
--- a/lib/workers/repository/onboarding/pr/pr-list.ts
+++ b/lib/workers/repository/onboarding/pr/pr-list.ts
@@ -1,6 +1,7 @@
 import type { RenovateConfig } from '../../../../config/types';
 import { logger } from '../../../../logger';
 import { emojify } from '../../../../util/emoji';
+import { regEx } from '../../../../util/regex';
 import type { BranchConfig } from '../../../types';
 
 export function getPrList(
@@ -17,7 +18,7 @@ export function getPrList(
   prDesc += branches.length > 1 ? `s:\n\n` : `:\n\n`;
 
   for (const branch of branches) {
-    const prTitleRe = /@([a-z]+\/[a-z]+)/;
+    const prTitleRe = regEx(/@([a-z]+\/[a-z]+)/); // TODO #12071
     prDesc += `<details>\n<summary>${branch.prTitle.replace(
       prTitleRe,
       '@&#8203;$1'
diff --git a/lib/workers/repository/process/lookup/current.ts b/lib/workers/repository/process/lookup/current.ts
index 83ff6103318f9f6def958761e47856fd35ce3f04..94f8d12fd4e7d99ebc807868e8652f27c8c88246 100644
--- a/lib/workers/repository/process/lookup/current.ts
+++ b/lib/workers/repository/process/lookup/current.ts
@@ -1,5 +1,6 @@
 import is from '@sindresorhus/is';
 import { logger } from '../../../../logger';
+import { regEx } from '../../../../util/regex';
 import type { VersioningApi } from '../../../../versioning/types';
 import type { LookupUpdateConfig } from './types';
 
@@ -19,7 +20,7 @@ export function getCurrentVersion(
     return currentValue;
   }
   if (versioning.isSingleVersion(currentValue)) {
-    return currentValue.replace(/=/g, '').trim();
+    return currentValue.replace(regEx(/=/g), '').trim();
   }
   logger.trace(`currentValue ${currentValue} is range`);
   let useVersions = allVersions.filter((v) =>
diff --git a/lib/workers/repository/process/lookup/index.ts b/lib/workers/repository/process/lookup/index.ts
index cf3143ade095b89fd53ca40d3ff5a1dc6783b38b..9f82d0191bd69aa5cba3b0704e7f1446c629e3cc 100644
--- a/lib/workers/repository/process/lookup/index.ts
+++ b/lib/workers/repository/process/lookup/index.ts
@@ -14,6 +14,7 @@ import { getRangeStrategy } from '../../../../manager';
 import { SkipReason } from '../../../../types';
 import { clone } from '../../../../util/clone';
 import { applyPackageRules } from '../../../../util/package-rules';
+import { regEx } from '../../../../util/regex';
 import * as allVersioning from '../../../../versioning';
 import { getBucket } from './bucket';
 import { getCurrentVersion } from './current';
@@ -284,7 +285,7 @@ export async function lookupUpdates(
     res.currentVersion = lockedVersion;
     res.fixedVersion = lockedVersion;
   } else if (currentValue && versioning.isSingleVersion(currentValue)) {
-    res.fixedVersion = currentValue.replace(/^=+/, '');
+    res.fixedVersion = currentValue.replace(regEx(/^=+/), '');
   }
   // Add digests if necessary
   if (supportsDigests(config)) {
diff --git a/lib/workers/repository/updates/branch-name.ts b/lib/workers/repository/updates/branch-name.ts
index e56e2da710fee664638ba40c2b368cd58e563a66..32211dca8760a65e7107f82def49f0059bb5f1d4 100644
--- a/lib/workers/repository/updates/branch-name.ts
+++ b/lib/workers/repository/updates/branch-name.ts
@@ -3,11 +3,12 @@ import hasha from 'hasha';
 import slugify from 'slugify';
 import type { RenovateConfig } from '../../../config/types';
 import { logger } from '../../../logger';
+import { regEx } from '../../../util/regex';
 import * as template from '../../../util/template';
 
 const MIN_HASH_LENGTH = 6;
 
-const RE_MULTIPLE_DASH = /--+/g;
+const RE_MULTIPLE_DASH = regEx(/--+/g);
 /**
  * Clean git branch name
  *
@@ -19,9 +20,10 @@ const RE_MULTIPLE_DASH = /--+/g;
  */
 function cleanBranchName(branchName: string): string {
   return cleanGitRef(branchName)
-    .replace(/^\.|\.$/, '') // leading or trailing dot
-    .replace(/\/\./g, '/') // leading dot after slash
-    .replace(/\s/g, '') // whitespace
+    .replace(regEx(/^\.|\.$/), '') // leading or trailing dot
+    .replace(regEx(/\/\./g), '/') // leading dot after slash
+    .replace(regEx(/\s/g), '') // whitespace
+    .replace(regEx(/[[\]?:\\^~]/g), '-') // massage out all these characters: : ? [ \ ^ ~
     .replace(RE_MULTIPLE_DASH, '-'); // chained dashes
 }
 
diff --git a/lib/workers/repository/updates/flatten.ts b/lib/workers/repository/updates/flatten.ts
index a6becaed1900ccc407e5d253d10aabb616ef1d00..22f3070e48c637d32c3fe84ab0c3eccf6e240170 100644
--- a/lib/workers/repository/updates/flatten.ts
+++ b/lib/workers/repository/updates/flatten.ts
@@ -7,6 +7,7 @@ import type { RenovateConfig } from '../../../config/types';
 import { getDefaultConfig } from '../../../datasource';
 import { get } from '../../../manager';
 import { applyPackageRules } from '../../../util/package-rules';
+import { regEx } from '../../../util/regex';
 import { parseUrl } from '../../../util/url';
 import type { BranchUpgradeConfig } from '../../types';
 import { generateBranchName } from './branch-name';
@@ -22,18 +23,18 @@ export function applyUpdateConfig(input: BranchUpgradeConfig): any {
     ? updateConfig.depName
         .replace('@types/', '')
         .replace('@', '')
-        .replace(/\//g, '-')
-        .replace(/\s+/g, '-')
-        .replace(/-+/, '-')
+        .replace(regEx(/\//g), '-') // TODO #12071
+        .replace(regEx(/\s+/g), '-') // TODO #12071
+        .replace(regEx(/-+/), '-')
         .toLowerCase()
     : undefined;
   if (updateConfig.sourceUrl) {
     const parsedSourceUrl = parseUrl(updateConfig.sourceUrl);
     if (parsedSourceUrl?.pathname) {
       updateConfig.sourceRepoSlug = parsedSourceUrl.pathname
-        .replace(/^\//, '') // remove leading slash
-        .replace(/\//g, '-') // change slashes to hyphens
-        .replace(/-+/g, '-'); // remove multiple hyphens
+        .replace(regEx(/^\//), '') // remove leading slash  // TODO #12071
+        .replace(regEx(/\//g), '-') // change slashes to hyphens   // TODO #12071
+        .replace(regEx(/-+/g), '-'); // remove multiple hyphens
     }
   }
   generateBranchName(updateConfig);
diff --git a/lib/workers/repository/updates/generate.ts b/lib/workers/repository/updates/generate.ts
index 05ea64a7e548dbc396ee8fb2856779b00c05d258..4ff74aff11938226c98e4f61b9f4eee4b99430b9 100644
--- a/lib/workers/repository/updates/generate.ts
+++ b/lib/workers/repository/updates/generate.ts
@@ -4,6 +4,7 @@ import semver from 'semver';
 import { mergeChildConfig } from '../../../config';
 import { CONFIG_SECRETS_EXPOSED } from '../../../constants/error-messages';
 import { logger } from '../../../logger';
+import { regEx } from '../../../util/regex';
 import { sanitize } from '../../../util/sanitize';
 import * as template from '../../../util/template';
 import type { BranchConfig, BranchUpgradeConfig } from '../../types';
@@ -160,8 +161,7 @@ export function generateBranchConfig(
       }
       upgrade.commitMessagePrefix = CommitMessage.formatPrefix(semanticPrefix);
       upgrade.toLowerCase =
-        // eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
-        upgrade.semanticCommitType.match(/[A-Z]/) === null &&
+        regEx(/[A-Z]/).exec(upgrade.semanticCommitType) === null && // TODO #12071
         !upgrade.semanticCommitType.startsWith(':');
     }
     // Compile a few times in case there are nested templates
@@ -176,9 +176,9 @@ export function generateBranchConfig(
       throw new Error(CONFIG_SECRETS_EXPOSED);
     }
     upgrade.commitMessage = upgrade.commitMessage.trim(); // Trim exterior whitespace
-    upgrade.commitMessage = upgrade.commitMessage.replace(/\s+/g, ' '); // Trim extra whitespace inside string
+    upgrade.commitMessage = upgrade.commitMessage.replace(regEx(/\s+/g), ' '); // Trim extra whitespace inside string // TODO #12071
     upgrade.commitMessage = upgrade.commitMessage.replace(
-      /to vv(\d)/,
+      regEx(/to vv(\d)/), // TODO #12071
       'to v$1'
     );
     if (upgrade.toLowerCase) {
@@ -200,7 +200,7 @@ export function generateBranchConfig(
       upgrade.prTitle = template
         .compile(upgrade.prTitle, upgrade)
         .trim()
-        .replace(/\s+/g, ' ');
+        .replace(regEx(/\s+/g), ' '); // TODO #12071
       // istanbul ignore if
       if (upgrade.prTitle !== sanitize(upgrade.prTitle)) {
         throw new Error(CONFIG_SECRETS_EXPOSED);
diff --git a/test/exec-util.ts b/test/exec-util.ts
index 6e12081cc5811cb5b9fdde88774b01f05bb01506..7e4d4e14972a4f058befa2302fd7cf44ad012930 100644
--- a/test/exec-util.ts
+++ b/test/exec-util.ts
@@ -3,6 +3,7 @@ import is from '@sindresorhus/is';
 import traverse from 'traverse';
 import { toUnix } from 'upath';
 import { ExecOptions } from '../lib/util/exec';
+import { regEx } from '../lib/util/regex';
 
 type CallOptions = ExecOptions | null | undefined;
 
@@ -29,7 +30,9 @@ export function execSnapshot(cmd: string, options?: CallOptions): ExecSnapshot {
   // eslint-disable-next-line array-callback-return
   return traverse(snapshot).map(function fixup(v) {
     if (is.string(v)) {
-      const val = v.replace(/\\(\w)/g, '/$1').replace(cwd, '/root/project');
+      const val = v
+        .replace(regEx(/\\(\w)/g), '/$1')
+        .replace(cwd, '/root/project'); // TODO #12071
       this.update(val);
     }
   });
diff --git a/tools/generate-imports.mjs b/tools/generate-imports.mjs
index 4633a0d09e64408b5b9086c282a887b1637475bd..e9508e15874902ff4b718e1836c62cb0a0c2bafc 100644
--- a/tools/generate-imports.mjs
+++ b/tools/generate-imports.mjs
@@ -73,7 +73,7 @@ async function generateData() {
   /** @type {string[]} */
   const contentMapAssignments = [];
   for (const file of files) {
-    const key = file.replace(/\\/g, '/');
+    const key = file.replace(/\\/g, '/'); // TODO #12071
 
     const rawFileContent = await fs.readFile(file, 'utf8');
     const value = JSON.stringify(rawFileContent);