From ee2e155f1fd032ec77bed942ee7b797137bb5dd2 Mon Sep 17 00:00:00 2001
From: RahulGautamSingh <rahultesnik@gmail.com>
Date: Tue, 30 Nov 2021 01:01:05 +0545
Subject: [PATCH] fix: modify regex to be re2 compatbile (#12778)

Co-authored-by: Rhys Arkins <rhys@arkins.net>
Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
---
 lib/datasource/crate/index.ts                 |  3 +-
 lib/datasource/go/releases-goproxy.ts         | 12 ++++----
 lib/datasource/maven/index.ts                 |  2 +-
 lib/datasource/npm/npmrc.ts                   |  3 +-
 lib/datasource/packagist/index.ts             |  3 +-
 lib/logger/cmd-serializer.ts                  |  2 +-
 lib/logger/err-serializer.ts                  |  2 +-
 lib/logger/pretty-stdout.ts                   |  2 +-
 lib/logger/utils.ts                           |  2 +-
 lib/manager/bazel/extract.ts                  |  2 +-
 lib/manager/bundler/extract.ts                |  5 ++--
 lib/manager/bundler/locked-version.ts         |  2 +-
 lib/manager/cake/index.ts                     |  8 +++---
 lib/manager/cocoapods/artifacts.ts            |  2 +-
 lib/manager/cocoapods/extract.ts              | 14 ++++++----
 lib/manager/dockerfile/extract.ts             |  4 +--
 lib/manager/gitlabci/extract.ts               | 22 +++++++++------
 lib/manager/gradle/shallow/tokenizer.ts       | 18 ++++++------
 lib/manager/helmv3/update.ts                  |  3 +-
 lib/manager/leiningen/extract.ts              |  9 +++---
 lib/manager/npm/extract/index.ts              | 28 +++++++++++--------
 lib/manager/npm/extract/pnpm.ts               |  2 +-
 .../npm/update/package-version/index.ts       |  3 +-
 lib/manager/nuget/extract.ts                  |  6 ++--
 lib/manager/terraform/lockfile/hash.ts        |  3 +-
 lib/manager/terraform/lockfile/util.ts        | 18 +++++++-----
 lib/manager/terraform/providers.ts            |  4 +--
 lib/manager/terraform/required-version.ts     |  5 ++--
 lib/platform/github/massage-markdown-links.ts |  4 +--
 lib/util/emoji.ts                             |  2 +-
 lib/util/http/gitea.ts                        |  2 +-
 lib/util/ignore.ts                            |  1 -
 lib/util/modules.ts                           |  4 +--
 lib/util/template/index.ts                    |  4 +--
 lib/util/url.ts                               |  4 +--
 lib/versioning/hashicorp/index.ts             |  4 +--
 lib/versioning/rez/pattern.ts                 |  8 +++---
 lib/versioning/ruby/index.ts                  |  7 +++--
 38 files changed, 127 insertions(+), 102 deletions(-)

diff --git a/lib/datasource/crate/index.ts b/lib/datasource/crate/index.ts
index b7c69137b9..acc4841adf 100644
--- a/lib/datasource/crate/index.ts
+++ b/lib/datasource/crate/index.ts
@@ -7,6 +7,7 @@ import * as memCache from '../../util/cache/memory';
 import { cache } from '../../util/cache/package/decorator';
 import { privateCacheDir, readFile } from '../../util/fs';
 import { simpleGitConfig } from '../../util/git/config';
+import { regEx } from '../../util/regex';
 import * as cargoVersioning from '../../versioning/cargo';
 import { Datasource } from '../datasource';
 import type { GetReleasesConfig, Release, ReleaseResult } from '../types';
@@ -143,7 +144,7 @@ export class CrateDatasource extends Datasource {
    * clone the repository.
    */
   private static cacheDirFromUrl(url: URL): string {
-    const proto = url.protocol.replace(/:$/, ''); // TODO #12070
+    const proto = url.protocol.replace(regEx(/:$/), '');
     const host = url.hostname;
     const hash = hasha(url.pathname, {
       algorithm: 'sha256',
diff --git a/lib/datasource/go/releases-goproxy.ts b/lib/datasource/go/releases-goproxy.ts
index 5acb7a080b..c35de8224f 100644
--- a/lib/datasource/go/releases-goproxy.ts
+++ b/lib/datasource/go/releases-goproxy.ts
@@ -37,9 +37,9 @@ export function parseGoproxy(
   }
 
   const result: GoproxyItem[] = input
-    .split(/([^,|]*(?:,|\|))/) // TODO: #12070
+    .split(regEx(/([^,|]*(?:,|\|))/))
     .filter(Boolean)
-    .map((s) => s.split(/(?=,|\|)/)) // TODO: #12070
+    .map((s) => s.split(/(?=,|\|)/)) // TODO: #12872 lookahead
     .map(([url, separator]) => ({
       url,
       fallback:
@@ -56,7 +56,7 @@ export function parseGoproxy(
 const lexer = moo.states({
   main: {
     separator: {
-      match: /\s*?,\s*?/, // TODO #12070
+      match: /\s*?,\s*?/, // TODO #12870
       value: (_: string) => '|',
     },
     asterisk: {
@@ -77,14 +77,14 @@ const lexer = moo.states({
       value: (s: string) => s.replace(regEx('\\.', 'g'), '\\.'),
     },
     escapedChar: {
-      match: /\\./, // TODO #12070
+      match: /\\./, // TODO #12870
       value: (s: string) => s.slice(1),
     },
   },
   characterRange: {
-    char: /[^\\\]\n]/, // TODO #12070
+    char: /[^\\\]\n]/, // TODO #12870
     escapedChar: {
-      match: /\\./, // TODO #12070
+      match: /\\./, // TODO #12870
       value: (s: string) => s.slice(1),
     },
     characterRangeEnd: {
diff --git a/lib/datasource/maven/index.ts b/lib/datasource/maven/index.ts
index 00f6d7198d..468ee1b157 100644
--- a/lib/datasource/maven/index.ts
+++ b/lib/datasource/maven/index.ts
@@ -306,7 +306,7 @@ export async function getReleases({
   registryUrl,
 }: GetReleasesConfig): Promise<ReleaseResult | null> {
   const dependency = getDependencyParts(lookupName);
-  const repoUrl = registryUrl.replace(/\/?$/, '/'); // TODO #12070
+  const repoUrl = registryUrl.replace(/\/?$/, '/'); // TODO #12071
 
   logger.debug(`Looking up ${dependency.display} in repository ${repoUrl}`);
 
diff --git a/lib/datasource/npm/npmrc.ts b/lib/datasource/npm/npmrc.ts
index 8a927ef636..cc3cf86f22 100644
--- a/lib/datasource/npm/npmrc.ts
+++ b/lib/datasource/npm/npmrc.ts
@@ -112,8 +112,7 @@ export function resolvePackage(packageName: string): PackageResolution {
     !authInfo &&
     npmrc &&
     npmrc._authToken &&
-    registryUrl.replace(regEx(/\/?$/), '/') ===
-      npmrc.registry?.replace(/\/?$/, '/') // TODO #12070
+    registryUrl.replace(/\/?$/, '/') === npmrc.registry?.replace(/\/?$/, '/') // TODO #12070
   ) {
     authInfo = { type: 'Bearer', token: npmrc._authToken };
   }
diff --git a/lib/datasource/packagist/index.ts b/lib/datasource/packagist/index.ts
index 12bda735ee..decb8d894b 100644
--- a/lib/datasource/packagist/index.ts
+++ b/lib/datasource/packagist/index.ts
@@ -6,6 +6,7 @@ import * as memCache from '../../util/cache/memory';
 import * as packageCache from '../../util/cache/package';
 import * as hostRules from '../../util/host-rules';
 import { Http, HttpOptions } from '../../util/http';
+import { regEx } from '../../util/regex';
 import * as composerVersioning from '../../versioning/composer';
 import type { GetReleasesConfig, ReleaseResult } from '../types';
 import type {
@@ -124,7 +125,7 @@ function extractDepReleases(versions: RegistryFile): ReleaseResult {
       dep.sourceUrl = release.source.url;
     }
     return {
-      version: version.replace(/^v/, ''), // TODO #12070
+      version: version.replace(regEx(/^v/), ''),
       gitRef: version,
       releaseTimestamp: release.time,
     };
diff --git a/lib/logger/cmd-serializer.ts b/lib/logger/cmd-serializer.ts
index ea7741c52b..5142e5c6ca 100644
--- a/lib/logger/cmd-serializer.ts
+++ b/lib/logger/cmd-serializer.ts
@@ -3,7 +3,7 @@ export default function cmdSerializer(
   cmd: string | string[]
 ): string | string[] {
   if (typeof cmd === 'string') {
-    return cmd.replace(/https:\/\/[^@]*@/g, 'https://**redacted**@'); // TODO #12070
+    return cmd.replace(/https:\/\/[^@]*@/g, 'https://**redacted**@'); // TODO #12874
   }
   return cmd;
 }
diff --git a/lib/logger/err-serializer.ts b/lib/logger/err-serializer.ts
index 23d81cc111..d33cc61460 100644
--- a/lib/logger/err-serializer.ts
+++ b/lib/logger/err-serializer.ts
@@ -12,7 +12,7 @@ export default function errSerializer(err: Error): any {
     const val = response[field];
     if (is.string(val)) {
       response[field] = val.replace(
-        /https:\/\/[^@]*?@/g, // TODO #12070 #12071
+        /https:\/\/[^@]*?@/g, // TODO #12874
         'https://**redacted**@'
       );
     }
diff --git a/lib/logger/pretty-stdout.ts b/lib/logger/pretty-stdout.ts
index fc97db214c..2259b88017 100644
--- a/lib/logger/pretty-stdout.ts
+++ b/lib/logger/pretty-stdout.ts
@@ -37,7 +37,7 @@ const levels: Record<number, string> = {
 
 export function indent(str: string, leading = false): string {
   const prefix = leading ? '       ' : '';
-  return prefix + str.split(/\r?\n/).join('\n       '); // TODO #12070
+  return prefix + str.split(/\r?\n/).join('\n       '); // TODO #12874
 }
 
 export function getMeta(rec: BunyanRecord): string {
diff --git a/lib/logger/utils.ts b/lib/logger/utils.ts
index 0bb5ffdaf3..cb0de0a395 100644
--- a/lib/logger/utils.ts
+++ b/lib/logger/utils.ts
@@ -184,7 +184,7 @@ export function withSanitizer(streamConfig: bunyan.Stream): bunyan.Stream {
       const result =
         streamConfig.type === 'raw'
           ? raw
-          : JSON.stringify(raw, bunyan.safeCycles()).replace(/\n?$/, '\n'); // TODO #12070
+          : JSON.stringify(raw, bunyan.safeCycles()).replace(/\n?$/, '\n'); // TODO #12874
       stream.write(result, enc, cb);
     };
 
diff --git a/lib/manager/bazel/extract.ts b/lib/manager/bazel/extract.ts
index 123f54d75f..1e97dbffff 100644
--- a/lib/manager/bazel/extract.ts
+++ b/lib/manager/bazel/extract.ts
@@ -51,7 +51,7 @@ function parseUrl(urlString: string): UrlParsedResult | null {
 
 const lexer = moo.states({
   main: {
-    lineComment: { match: /#.*?$/ }, // TODO #12070
+    lineComment: { match: /#.*?$/ }, // TODO #12870
     leftParen: { match: '(' },
     rightParen: { match: ')' },
     longDoubleQuoted: {
diff --git a/lib/manager/bundler/extract.ts b/lib/manager/bundler/extract.ts
index 16032b92f9..2d8ccd655c 100644
--- a/lib/manager/bundler/extract.ts
+++ b/lib/manager/bundler/extract.ts
@@ -38,8 +38,9 @@ export async function extractPackageFile(
     if (rubyMatch) {
       res.constraints = { ruby: rubyMatch[1] };
     }
-    const gemMatchRegex =
-      /^\s*gem\s+(['"])(?<depName>[^'"]+)\1(\s*,\s*(?<currentValue>(['"])[^'"]+\5(\s*,\s*\5[^'"]+\5)?))?/; // TODO #12070 #12071
+    const gemMatchRegex = regEx(
+      `^\\s*gem\\s+(['"])(?<depName>[^'"]+)(['"])(\\s*,\\s*(?<currentValue>(['"])[^'"]+['"](\\s*,\\s*['"][^'"]+['"])?))?`
+    ); // TODO #12071
     const gemMatch = gemMatchRegex.exec(line);
     if (gemMatch) {
       const dep: PackageDependency = {
diff --git a/lib/manager/bundler/locked-version.ts b/lib/manager/bundler/locked-version.ts
index c18ceb244d..e2d786c9e0 100644
--- a/lib/manager/bundler/locked-version.ts
+++ b/lib/manager/bundler/locked-version.ts
@@ -1,7 +1,7 @@
 import { logger } from '../../logger';
 import { isVersion } from '../../versioning/ruby';
 
-const DEP_REGEX = new RegExp('(?<=\\().*(?=\\))'); // TODO #12070
+const DEP_REGEX = new RegExp('(?<=\\().*(?=\\))'); // TODO #12872  (?<=re)	after text matching
 export function extractLockFileEntries(
   lockFileContent: string
 ): Map<string, string> {
diff --git a/lib/manager/cake/index.ts b/lib/manager/cake/index.ts
index c469a3e29f..5d2a05166c 100644
--- a/lib/manager/cake/index.ts
+++ b/lib/manager/cake/index.ts
@@ -13,13 +13,13 @@ export const defaultConfig = {
 
 const lexer = moo.states({
   main: {
-    lineComment: { match: /\/\/.*?$/ }, // TODO #12070
-    multiLineComment: { match: /\/\*[^]*?\*\//, lineBreaks: true }, // TODO #12070
+    lineComment: { match: /\/\/.*?$/ }, // TODO #12870
+    multiLineComment: { match: /\/\*[^]*?\*\//, lineBreaks: true }, // TODO #12870
     dependency: {
-      match: /^#(?:addin|tool|module|load|l)\s+(?:nuget|dotnet):.*$/, // TODO #12070
+      match: /^#(?:addin|tool|module|load|l)\s+(?:nuget|dotnet):.*$/, // TODO #12870
     },
     dependencyQuoted: {
-      match: /^#(?:addin|tool|module|load|l)\s+"(?:nuget|dotnet):[^"]+"\s*$/, // TODO #12070
+      match: /^#(?:addin|tool|module|load|l)\s+"(?:nuget|dotnet):[^"]+"\s*$/, // TODO #12870
       value: (s: string) => s.trim().slice(1, -1),
     },
     unknown: moo.fallback,
diff --git a/lib/manager/cocoapods/artifacts.ts b/lib/manager/cocoapods/artifacts.ts
index 3fc124fa06..7c745cfb5d 100644
--- a/lib/manager/cocoapods/artifacts.ts
+++ b/lib/manager/cocoapods/artifacts.ts
@@ -13,7 +13,7 @@ import { getRepoStatus } from '../../util/git';
 import { regEx } from '../../util/regex';
 import type { UpdateArtifact, UpdateArtifactsResult } from '../types';
 
-const pluginRegex = /^\s*plugin\s*(['"])(?<plugin>[^'"]+)\1/; // TODO #12070
+const pluginRegex = regEx(`^\\s*plugin\\s*(['"])(?<plugin>[^'"]+)(['"])`);
 
 function getPluginCommands(content: string): string[] {
   const result = new Set<string>();
diff --git a/lib/manager/cocoapods/extract.ts b/lib/manager/cocoapods/extract.ts
index 09ce166170..84306227bd 100644
--- a/lib/manager/cocoapods/extract.ts
+++ b/lib/manager/cocoapods/extract.ts
@@ -10,12 +10,14 @@ import type { PackageDependency, PackageFile } from '../types';
 import type { ParsedLine } from './types';
 
 const regexMappings = [
-  /^\s*pod\s+(['"])(?<spec>[^'"/]+)(\/(?<subspec>[^'"]+))?\1/, // TODO #12070
-  /^\s*pod\s+(['"])[^'"]+\1\s*,\s*(['"])(?<currentValue>[^'"]+)\2\s*$/, // TODO #12070
-  /,\s*:git\s*=>\s*(['"])(?<git>[^'"]+)\1/, // TODO #12070
-  /,\s*:tag\s*=>\s*(['"])(?<tag>[^'"]+)\1/, // TODO #12070
-  /,\s*:path\s*=>\s*(['"])(?<path>[^'"]+)\1/, // TODO #12070
-  /^\s*source\s*(['"])(?<source>[^'"]+)\1/, // TODO #12070
+  regEx(`^\\s*pod\\s+(['"])(?<spec>[^'"/]+)(\\/(?<subspec>[^'"]+))?(['"])`),
+  regEx(
+    `^\\s*pod\\s+(['"])[^'"]+(['"])\\s*,\\s*(['"])(?<currentValue>[^'"]+)(['"])\\s*$`
+  ),
+  regEx(`,\\s*:git\\s*=>\\s*(['"])(?<git>[^'"]+)(['"])`),
+  regEx(`,\\s*:tag\\s*=>\\s*(['"])(?<tag>[^'"]+)(['"])`),
+  regEx(`,\\s*:path\\s*=>\\s*(['"])(?<path>[^'"]+)(['"])`),
+  regEx(`^\\s*source\\s*(['"])(?<source>[^'"]+)(['"])`),
 ];
 
 export function parseLine(line: string): ParsedLine {
diff --git a/lib/manager/dockerfile/extract.ts b/lib/manager/dockerfile/extract.ts
index 0053192215..778e9702ef 100644
--- a/lib/manager/dockerfile/extract.ts
+++ b/lib/manager/dockerfile/extract.ts
@@ -153,7 +153,7 @@ export function extractPackageFile(content: string): PackageFile | null {
   const stageNames: string[] = [];
 
   const fromMatches = content.matchAll(
-    /^[ \t]*FROM(?:\\\r?\n| |\t|#.*?\r?\n|[ \t]--[a-z]+=\S+?)*[ \t](?<image>\S+)(?:(?:\\\r?\n| |\t|#.*\r?\n)+as[ \t]+(?<name>\S+))?/gim // TODO #12070
+    /^[ \t]*FROM(?:\\\r?\n| |\t|#.*?\r?\n|[ \t]--[a-z]+=\S+?)*[ \t](?<image>\S+)(?:(?:\\\r?\n| |\t|#.*\r?\n)+as[ \t]+(?<name>\S+))?/gim // TODO #12070 complex for re2 has too many not supported groups
   );
 
   for (const fromMatch of fromMatches) {
@@ -180,7 +180,7 @@ export function extractPackageFile(content: string): PackageFile | null {
   }
 
   const copyFromMatches = content.matchAll(
-    /^[ \t]*COPY(?:\\\r?\n| |\t|#.*\r?\n|[ \t]--[a-z]+=\w+?)*[ \t]--from=(?<image>\S+)/gim // TODO #12070
+    /^[ \t]*COPY(?:\\\r?\n| |\t|#.*\r?\n|[ \t]--[a-z]+=\w+?)*[ \t]--from=(?<image>\S+)/gim // TODO #12070 complex for re2 has too many not supported groups
   );
 
   for (const copyFromMatch of copyFromMatches) {
diff --git a/lib/manager/gitlabci/extract.ts b/lib/manager/gitlabci/extract.ts
index 23d98d30ac..14a478b453 100644
--- a/lib/manager/gitlabci/extract.ts
+++ b/lib/manager/gitlabci/extract.ts
@@ -2,17 +2,21 @@ import is from '@sindresorhus/is';
 import { load } from 'js-yaml';
 import { logger } from '../../logger';
 import { readLocalFile } from '../../util/fs';
+import { regEx } from '../../util/regex';
 import { getDep } from '../dockerfile/extract';
 import type { ExtractConfig, PackageDependency, PackageFile } from '../types';
 import type { GitlabPipeline } from './types';
 import { replaceReferenceTags } from './utils';
 
-const commentsRe = /^\s*#/; // TODO #12070
-const whitespaceRe = /^(?<whitespace>\s*)/; // TODO #12070
-const imageRe =
-  /^(?<whitespace>\s*)image:(?:\s+['"]?(?<image>[^\s'"]+)['"]?)?\s*$/; // TODO #12070
-const nameRe = /^\s*name:\s+['"]?(?<depName>[^\s'"]+)['"]?\s*$/; // TODO #12070
-const serviceRe = /^\s*-\s*(?:name:\s+)?['"]?(?<depName>[^\s'"]+)['"]?\s*$/; // TODO #12070
+const commentsRe = regEx(/^\s*#/);
+const whitespaceRe = regEx(`^(?<whitespace>\\s*)`);
+const imageRe = regEx(
+  `^(?<whitespace>\\s*)image:(?:\\s+['"]?(?<image>[^\\s'"]+)['"]?)?\\s*$`
+);
+const nameRe = regEx(`^\\s*name:\\s+['"]?(?<depName>[^\\s'"]+)['"]?\\s*$`);
+const serviceRe = regEx(
+  `^\\s*-\\s*(?:name:\\s+)?['"]?(?<depName>[^\\s'"]+)['"]?\\s*$`
+);
 function skipCommentLines(
   lines: string[],
   lineNumber: number
@@ -61,7 +65,7 @@ export function extractPackageFile(content: string): PackageFile | null {
           }
         }
       }
-      const services = /^\s*services:\s*$/.test(line); // TODO #12071  #12070
+      const services = regEx(/^\s*services:\s*$/).test(line); // TODO #12071
       if (services) {
         logger.trace(`Matched services on line ${lineNumber}`);
         let foundImage: boolean;
@@ -120,7 +124,7 @@ export async function extractAllPackageFiles(
     if (is.array(doc?.include)) {
       for (const includeObj of doc.include) {
         if (is.string(includeObj.local)) {
-          const fileObj = includeObj.local.replace(/^\//, ''); // TODO #12071 #12070
+          const fileObj = includeObj.local.replace(regEx(/^\//), '');
           if (!seen.has(fileObj)) {
             seen.add(fileObj);
             filesToExamine.push(fileObj);
@@ -128,7 +132,7 @@ export async function extractAllPackageFiles(
         }
       }
     } else if (is.string(doc?.include)) {
-      const fileObj = doc.include.replace(/^\//, ''); // TODO #12071  #12070
+      const fileObj = doc.include.replace(regEx(/^\//), '');
       if (!seen.has(fileObj)) {
         seen.add(fileObj);
         filesToExamine.push(fileObj);
diff --git a/lib/manager/gradle/shallow/tokenizer.ts b/lib/manager/gradle/shallow/tokenizer.ts
index 728fcbe2a8..b599d41f9b 100644
--- a/lib/manager/gradle/shallow/tokenizer.ts
+++ b/lib/manager/gradle/shallow/tokenizer.ts
@@ -3,7 +3,7 @@ import { regEx } from '../../../util/regex';
 import { TokenType } from './common';
 import type { StringInterpolation, Token } from './types';
 
-const escapedCharRegex = /\\['"bfnrt\\]/; // TODO #12070
+const escapedCharRegex = /\\['"bfnrt\\]/; // TODO #12870
 const escapedChars = {
   [TokenType.EscapedChar]: {
     match: escapedCharRegex,
@@ -24,17 +24,17 @@ const escapedChars = {
 const lexer = moo.states({
   // Top-level Groovy lexemes
   main: {
-    [TokenType.LineComment]: { match: /\/\/.*?$/ }, // TODO #12070
-    [TokenType.MultiComment]: { match: /\/\*[^]*?\*\//, lineBreaks: true }, // TODO #12070
-    [TokenType.Newline]: { match: /\r?\n/, lineBreaks: true }, // TODO #12070
-    [TokenType.Space]: { match: /[ \t\r]+/ }, // TODO #12070
+    [TokenType.LineComment]: { match: /\/\/.*?$/ }, // TODO #12870
+    [TokenType.MultiComment]: { match: /\/\*[^]*?\*\//, lineBreaks: true }, // TODO #12870
+    [TokenType.Newline]: { match: /\r?\n/, lineBreaks: true }, // TODO #12870
+    [TokenType.Space]: { match: /[ \t\r]+/ }, // TODO #12870
     [TokenType.Semicolon]: ';',
     [TokenType.Colon]: ':',
     [TokenType.Dot]: '.',
     [TokenType.Comma]: ',',
-    [TokenType.Operator]: /(?:==|\+=?|-=?|\/=?|\*\*?|\.+|:)/, // TODO #12070
+    [TokenType.Operator]: /(?:==|\+=?|-=?|\/=?|\*\*?|\.+|:)/, // TODO #12870
     [TokenType.Assignment]: '=',
-    [TokenType.Word]: { match: /[a-zA-Z$_][a-zA-Z0-9$_]+/ }, // TODO #12070
+    [TokenType.Word]: { match: /[a-zA-Z$_][a-zA-Z0-9$_]+/ }, // TODO #12870
     [TokenType.LeftParen]: { match: '(' },
     [TokenType.RightParen]: { match: ')' },
     [TokenType.LeftBracket]: { match: '[' },
@@ -86,12 +86,12 @@ const lexer = moo.states({
     variable: {
       // Supported: ${foo}, $foo, ${ foo.bar.baz }, $foo.bar.baz
       match:
-        /\${\s*[a-zA-Z_][a-zA-Z0-9_]*(?:\s*\.\s*[a-zA-Z_][a-zA-Z0-9_]*)*\s*}|\$[a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*/, // TODO #12070
+        /\${\s*[a-zA-Z_][a-zA-Z0-9_]*(?:\s*\.\s*[a-zA-Z_][a-zA-Z0-9_]*)*\s*}|\$[a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*/, // TODO #12870
       value: (x: string): string =>
         x.replace(regEx(/^\${?\s*/), '').replace(regEx(/\s*}$/), ''),
     },
     [TokenType.IgnoredInterpolationStart]: {
-      match: /\${/, // TODO #12070
+      match: /\${/, // TODO #12870
       push: TokenType.IgnoredInterpolationStart,
     },
     [TokenType.Chars]: moo.fallback,
diff --git a/lib/manager/helmv3/update.ts b/lib/manager/helmv3/update.ts
index 6f02c15eb3..a965beb322 100644
--- a/lib/manager/helmv3/update.ts
+++ b/lib/manager/helmv3/update.ts
@@ -1,5 +1,6 @@
 import { ReleaseType, inc } from 'semver';
 import { logger } from '../../logger';
+import { regEx } from '../../util/regex';
 import type { BumpPackageVersionResult } from '../types';
 
 export function bumpPackageVersion(
@@ -20,7 +21,7 @@ export function bumpPackageVersion(
     }
     logger.debug({ newChartVersion });
     bumpedContent = content.replace(
-      /^(?<version>version:\s*).*$/m, // TODO #12070
+      regEx(`^(?<version>version:\\s*).*$`, 'm'),
       `$<version>${newChartVersion}`
     );
     if (bumpedContent === content) {
diff --git a/lib/manager/leiningen/extract.ts b/lib/manager/leiningen/extract.ts
index 1986c36e74..9d83ff1022 100644
--- a/lib/manager/leiningen/extract.ts
+++ b/lib/manager/leiningen/extract.ts
@@ -4,7 +4,7 @@ import type { PackageDependency, PackageFile } from '../types';
 import type { ExtractContext, ExtractedVariables } from './types';
 
 export function trimAtKey(str: string, kwName: string): string | null {
-  const regex = new RegExp(`:${kwName}(?=\\s)`); // TODO #12070
+  const regex = new RegExp(`:${kwName}(?=\\s)`); // TODO #12872 lookahead
   const keyOffset = str.search(regex);
   if (keyOffset < 0) {
     return null;
@@ -105,7 +105,7 @@ function extractLeinRepos(content: string): string[] {
   const result = [];
 
   const repoContent = trimAtKey(
-    content.replace(/;;.*(?=[\r\n])/g, ''), // get rid of comments // TODO #12070
+    content.replace(/;;.*(?=[\r\n])/g, ''), // get rid of comments // TODO #12872 lookahead
     'repositories'
   );
 
@@ -125,10 +125,11 @@ function extractLeinRepos(content: string): string[] {
       }
     }
     const repoSectionContent = repoContent.slice(0, endIdx);
-    const matches = repoSectionContent.match(/"https?:\/\/[^"]*"/g) || []; // TODO #12070
+    const matches =
+      repoSectionContent.match(regEx(/"https?:\/\/[^"]*"/g)) || [];
     const urls = matches.map((x) =>
       x.replace(regEx(/^"/), '').replace(regEx(/"$/), '')
-    ); // TODO #12071
+    );
     urls.forEach((url) => result.push(url));
   }
 
diff --git a/lib/manager/npm/extract/index.ts b/lib/manager/npm/extract/index.ts
index 143c8de05b..4ed11b1940 100644
--- a/lib/manager/npm/extract/index.ts
+++ b/lib/manager/npm/extract/index.ts
@@ -27,12 +27,13 @@ function parseDepName(depType: string, key: string): string {
     return key;
   }
 
-  const [, depName] = /((?:@[^/]+\/)?[^/@]+)$/.exec(key) ?? []; // TODO #12070
+  const [, depName] = regEx(/((?:@[^/]+\/)?[^/@]+)$/).exec(key) ?? [];
   return depName;
 }
 
-const RE_REPOSITORY_GITHUB_SSH_FORMAT =
-  /(?:git@)github.com:([^/]+)\/([^/.]+)(?:\.git)?/; // TODO #12070
+const RE_REPOSITORY_GITHUB_SSH_FORMAT = regEx(
+  /(?:git@)github.com:([^/]+)\/([^/.]+)(?:\.git)?/
+);
 
 export async function extractPackageFile(
   content: string,
@@ -107,11 +108,14 @@ export async function extractPackageFile(
     } else {
       npmrc = config.npmrc || '';
       if (npmrc.length) {
-        npmrc = npmrc.replace(/\n?$/, '\n'); // TODO #12070
+        npmrc = npmrc.replace(/\n?$/, '\n'); // TODO #12070 add \n to front when used with re2
       }
       if (repoNpmrc?.includes('package-lock')) {
         logger.debug('Stripping package-lock setting from .npmrc');
-        repoNpmrc = repoNpmrc.replace(/(^|\n)package-lock.*?(\n|$)/g, '\n'); // TODO #12070
+        repoNpmrc = repoNpmrc.replace(
+          regEx(/(^|\n)package-lock.*?(\n|$)/g),
+          '\n'
+        );
       }
       if (repoNpmrc.includes('=${') && !GlobalConfig.get('exposeAllEnv')) {
         logger.debug(
@@ -280,10 +284,10 @@ export async function extractPackageFile(
     const matchUrlSshFormat = RE_REPOSITORY_GITHUB_SSH_FORMAT.exec(depNamePart);
     if (matchUrlSshFormat === null) {
       githubOwnerRepo = depNamePart
-        .replace(/^github:/, '') // TODO #12070
-        .replace(/^git\+/, '') // TODO #12070
-        .replace(/^https:\/\/github\.com\//, '') // TODO #12070
-        .replace(/\.git$/, ''); // TODO #12070
+        .replace(regEx(/^github:/), '')
+        .replace(regEx(/^git\+/), '')
+        .replace(regEx(/^https:\/\/github\.com\//), '')
+        .replace(regEx(/\.git$/), '');
       const githubRepoSplit = githubOwnerRepo.split('/');
       if (githubRepoSplit.length !== 2) {
         dep.skipReason = SkipReason.UnknownVersion;
@@ -295,7 +299,7 @@ export async function extractPackageFile(
       githubRepo = matchUrlSshFormat[2];
       githubOwnerRepo = `${githubOwner}/${githubRepo}`;
     }
-    const githubValidRegex = /^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}$/; // TODO #12070
+    const githubValidRegex = /^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}$/; // TODO #12872 lookahead
     if (
       !githubValidRegex.test(githubOwner) ||
       !githubValidRegex.test(githubRepo)
@@ -310,8 +314,8 @@ export async function extractPackageFile(
       dep.lookupName = githubOwnerRepo;
       dep.pinDigests = false;
     } else if (
-      /^[0-9a-f]{7}$/.test(depRefPart) || // TODO #12070
-      /^[0-9a-f]{40}$/.test(depRefPart) // TODO #12070
+      regEx(/^[0-9a-f]{7}$/).test(depRefPart) ||
+      regEx(/^[0-9a-f]{40}$/).test(depRefPart)
     ) {
       dep.currentRawValue = dep.currentValue;
       dep.currentValue = null;
diff --git a/lib/manager/npm/extract/pnpm.ts b/lib/manager/npm/extract/pnpm.ts
index 64475e6eba..54be1d398b 100644
--- a/lib/manager/npm/extract/pnpm.ts
+++ b/lib/manager/npm/extract/pnpm.ts
@@ -105,7 +105,7 @@ export async function detectPnpmWorkspaces(
       packageFilters !== null &&
       matchesAnyPattern(
         packageFile,
-        packageFilters.map((filter) => filter.replace(/\/?$/, '/package.json')) // TODO #12070 #12071
+        packageFilters.map((filter) => filter.replace(/\/?$/, '/package.json')) // TODO #12070
       );
     if (isPackageInWorkspace) {
       p.pnpmShrinkwrap = lockFilePath;
diff --git a/lib/manager/npm/update/package-version/index.ts b/lib/manager/npm/update/package-version/index.ts
index 0689cd9109..c28065f635 100644
--- a/lib/manager/npm/update/package-version/index.ts
+++ b/lib/manager/npm/update/package-version/index.ts
@@ -1,5 +1,6 @@
 import { ReleaseType, inc } from 'semver';
 import { logger } from '../../../../logger';
+import { regEx } from '../../../../util/regex';
 import type { BumpPackageVersionResult } from '../../../types';
 
 export function bumpPackageVersion(
@@ -31,7 +32,7 @@ export function bumpPackageVersion(
     }
     logger.debug({ newPjVersion });
     bumpedContent = content.replace(
-      /(?<version>"version":\s*")[^"]*/, // TODO #12070
+      regEx(`(?<version>"version":\\s*")[^"]*`),
       `$<version>${newPjVersion}`
     );
     if (bumpedContent === content) {
diff --git a/lib/manager/nuget/extract.ts b/lib/manager/nuget/extract.ts
index fb4f4fc130..5c14279e91 100644
--- a/lib/manager/nuget/extract.ts
+++ b/lib/manager/nuget/extract.ts
@@ -4,6 +4,7 @@ import * as datasourceNuget from '../../datasource/nuget';
 import { logger } from '../../logger';
 import { getSiblingFileName, localPathExists } from '../../util/fs';
 import { hasKey } from '../../util/object';
+import { regEx } from '../../util/regex';
 import type { ExtractConfig, PackageDependency, PackageFile } from '../types';
 import { extractMsbuildGlobalManifest } from './extract/global-manifest';
 import type { DotnetToolsManifest } from './types';
@@ -20,8 +21,9 @@ import { getConfiguredRegistries } from './util';
  * The update of the right boundary does not make sense regarding to the lowest version restore rule,
  * so we don't include it in the extracting regexp
  */
-const checkVersion =
-  /^\s*(?:[[])?(?:(?<currentValue>[^"(,[\]]+)\s*(?:,\s*[)\]]|])?)\s*$/; // TODO #12070
+const checkVersion = regEx(
+  `^\\s*(?:[[])?(?:(?<currentValue>[^"(,[\\]]+)\\s*(?:,\\s*[)\\]]|])?)\\s*$`
+);
 const elemNames = new Set([
   'PackageReference',
   'PackageVersion',
diff --git a/lib/manager/terraform/lockfile/hash.ts b/lib/manager/terraform/lockfile/hash.ts
index 94799f362c..f20b2ec0c4 100644
--- a/lib/manager/terraform/lockfile/hash.ts
+++ b/lib/manager/terraform/lockfile/hash.ts
@@ -9,6 +9,7 @@ import { cache } from '../../../util/cache/package/decorator';
 import * as fs from '../../../util/fs';
 import { ensureCacheDir } from '../../../util/fs';
 import { Http } from '../../../util/http';
+import { regEx } from '../../../util/regex';
 
 export class TerraformProviderHash {
   static http = new Http(TerraformProviderDatasource.id);
@@ -31,7 +32,7 @@ export class TerraformProviderHash {
 
       // add double space, the filename and a new line char
       rootHash.update('  ');
-      const fileName = file.replace(/^.*[\\/]/, ''); // TODO #12070
+      const fileName = file.replace(regEx(/^.*[\\/]/), ''); // TODO #12071
       rootHash.update(fileName);
       rootHash.update('\n');
     }
diff --git a/lib/manager/terraform/lockfile/util.ts b/lib/manager/terraform/lockfile/util.ts
index d6bedc5d2c..4e257557c3 100644
--- a/lib/manager/terraform/lockfile/util.ts
+++ b/lib/manager/terraform/lockfile/util.ts
@@ -1,4 +1,5 @@
 import { getSiblingFileName, readLocalFile } from '../../../util/fs';
+import { regEx } from '../../../util/regex';
 import { get as getVersioning } from '../../../versioning';
 import type { UpdateArtifactsResult } from '../../types';
 import type {
@@ -8,13 +9,16 @@ import type {
   ProviderSlice,
 } from './types';
 
-const providerStartLineRegex =
-  /^provider "(?<registryUrl>[^/]*)\/(?<namespace>[^/]*)\/(?<depName>[^/]*)"/; // TODO #12070
-const versionLineRegex =
-  /^(?<prefix>[\s]*version[\s]*=[\s]*")(?<version>[^"']+)(?<suffix>".*)$/; // TODO #12070
-const constraintLineRegex =
-  /^(?<prefix>[\s]*constraints[\s]*=[\s]*")(?<constraint>[^"']+)(?<suffix>".*)$/; // TODO #12070
-const hashLineRegex = /^(?<prefix>\s*")(?<hash>[^"]+)(?<suffix>",.*)$/; // TODO #12070
+const providerStartLineRegex = regEx(
+  `^provider "(?<registryUrl>[^/]*)\\/(?<namespace>[^/]*)\\/(?<depName>[^/]*)"`
+);
+const versionLineRegex = regEx(
+  `^(?<prefix>[\\s]*version[\\s]*=[\\s]*")(?<version>[^"']+)(?<suffix>".*)$`
+);
+const constraintLineRegex = regEx(
+  `^(?<prefix>[\\s]*constraints[\\s]*=[\\s]*")(?<constraint>[^"']+)(?<suffix>".*)$`
+);
+const hashLineRegex = regEx(`^(?<prefix>\\s*")(?<hash>[^"]+)(?<suffix>",.*)$`);
 
 const lockFile = '.terraform.lock.hcl';
 
diff --git a/lib/manager/terraform/providers.ts b/lib/manager/terraform/providers.ts
index 229395dc1d..f82e1a05fa 100644
--- a/lib/manager/terraform/providers.ts
+++ b/lib/manager/terraform/providers.ts
@@ -42,8 +42,8 @@ export function extractTerraformProvider(
     // istanbul ignore else
     if (is.string(line)) {
       // `{` will be counted wit +1 and `}` with -1. Therefore if we reach braceCounter == 0. We have found the end of the terraform block
-      const openBrackets = (line.match(/\{/g) || []).length; // TODO #12071 #12070
-      const closedBrackets = (line.match(/\}/g) || []).length; // TODO #12071 #12070
+      const openBrackets = (line.match(regEx(/\{/g)) || []).length; // TODO #12071
+      const closedBrackets = (line.match(regEx(/\}/g)) || []).length; // TODO #12071
       braceCounter = braceCounter + openBrackets - closedBrackets;
 
       // only update fields inside the root block
diff --git a/lib/manager/terraform/required-version.ts b/lib/manager/terraform/required-version.ts
index 1de63c941f..12f5ad4bdf 100644
--- a/lib/manager/terraform/required-version.ts
+++ b/lib/manager/terraform/required-version.ts
@@ -1,5 +1,6 @@
 import * as datasourceGithubTags from '../../datasource/github-tags';
 import { logger } from '../../logger';
+import { regEx } from '../../util/regex';
 import type { PackageDependency } from '../types';
 import { TerraformDependencyTypes } from './common';
 import type { ExtractionResult } from './types';
@@ -20,8 +21,8 @@ export function extractTerraformRequiredVersion(
 
     const line = lines[lineNumber];
     // `{` will be counted wit +1 and `}` with -1. Therefore if we reach braceCounter == 0. We have found the end of the terraform block
-    const openBrackets = (line.match(/\{/g) || []).length; // TODO #12070
-    const closedBrackets = (line.match(/\}/g) || []).length; // TODO #12070
+    const openBrackets = (line.match(regEx(/\{/g)) || []).length;
+    const closedBrackets = (line.match(regEx(/\}/g)) || []).length;
     braceCounter = braceCounter + openBrackets - closedBrackets;
 
     const kvMatch = keyValueExtractionRegex.exec(line);
diff --git a/lib/platform/github/massage-markdown-links.ts b/lib/platform/github/massage-markdown-links.ts
index cbff68d00f..84391ff764 100644
--- a/lib/platform/github/massage-markdown-links.ts
+++ b/lib/platform/github/massage-markdown-links.ts
@@ -12,10 +12,10 @@ interface UrlMatch {
 }
 
 const urlRegex =
-  /(?:https?:)?(?:\/\/)?(?:www\.)?(?<!api\.)(?:to)?github\.com\/[-_a-z0-9]+\/[-_a-z0-9]+\/(?:discussions|issues|pull)\/[0-9]+(?:#[-_a-z0-9]+)?/i; // TODO #12070
+  /(?:https?:)?(?:\/\/)?(?:www\.)?(?<!api\.)(?:to)?github\.com\/[-_a-z0-9]+\/[-_a-z0-9]+\/(?:discussions|issues|pull)\/[0-9]+(?:#[-_a-z0-9]+)?/i; // TODO #12872 (?<!re) after text not matching
 
 function massageLink(input: string): string {
-  return input.replace(regEx(/(?:to)?github\.com/i), 'togithub.com'); // TODO #12071
+  return input.replace(regEx(/(?:to)?github\.com/i), 'togithub.com');
 }
 
 function collectLinkPosition(input: string, matches: UrlMatch[]): Plugin {
diff --git a/lib/util/emoji.ts b/lib/util/emoji.ts
index 7f6edaa8de..d7f1166ed5 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'); // TODO #12070
+const emojiRegex = new RegExp(`(?:${emojiRegexSrc.join('|')})`, 'g'); // TODO #12070 cannot figure it out
 const excludedModifiers = new Set([
   '20E3',
   '200D',
diff --git a/lib/util/http/gitea.ts b/lib/util/http/gitea.ts
index 2631c5e0d7..24d618fdeb 100644
--- a/lib/util/http/gitea.ts
+++ b/lib/util/http/gitea.ts
@@ -4,7 +4,7 @@ import { Http, HttpOptions, HttpResponse, InternalHttpOptions } from '.';
 
 let baseUrl: string;
 export const setBaseUrl = (newBaseUrl: string): void => {
-  baseUrl = newBaseUrl.replace(/\/*$/, '/'); // TODO #12070 #12071
+  baseUrl = newBaseUrl.replace(/\/*$/, '/'); // TODO #12070
 };
 
 export interface GiteaHttpOptions extends InternalHttpOptions {
diff --git a/lib/util/ignore.ts b/lib/util/ignore.ts
index 6ef218d970..fc16a2e137 100644
--- a/lib/util/ignore.ts
+++ b/lib/util/ignore.ts
@@ -3,7 +3,6 @@ import { regEx } from './regex';
 
 export function isSkipComment(comment?: string): boolean {
   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/modules.ts b/lib/util/modules.ts
index 9d988ac109..5b3b9fc27d 100644
--- a/lib/util/modules.ts
+++ b/lib/util/modules.ts
@@ -3,8 +3,8 @@ import { join, normalizeTrim } from 'upath';
 import { regEx } from './regex';
 
 function relatePath(here: string, there: string): string {
-  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
+  const thereParts = normalizeTrim(there).split(regEx(/[\\/]/));
+  const hereParts = normalizeTrim(here).split(regEx(/[\\/]/));
 
   let idx = 0;
   while (
diff --git a/lib/util/template/index.ts b/lib/util/template/index.ts
index a4e84d6025..a449097b3f 100644
--- a/lib/util/template/index.ts
+++ b/lib/util/template/index.ts
@@ -13,7 +13,7 @@ handlebars.registerHelper('stringToPrettyJSON', (input: string): string =>
 // istanbul ignore next
 handlebars.registerHelper(
   'replace',
-  (find, replace, context) => context.replace(new RegExp(find, 'g'), replace) // TODO #12070
+  (find, replace, context) => context.replace(new RegExp(find, 'g'), replace) // TODO #12873
 );
 
 export const exposedConfigOptions = [
@@ -147,7 +147,7 @@ function getFilteredObject(input: CompileInput): any {
   return res;
 }
 
-const templateRegex = /{{(#(if|unless) )?([a-zA-Z]+)}}/g; // TODO #12070
+const templateRegex = /{{(#(if|unless) )?([a-zA-Z]+)}}/g; // TODO #12873
 
 export function compile(
   template: string,
diff --git a/lib/util/url.ts b/lib/util/url.ts
index 99677b269a..25a23c31f9 100644
--- a/lib/util/url.ts
+++ b/lib/util/url.ts
@@ -15,11 +15,11 @@ export function ensurePathPrefix(url: string, prefix: string): string {
 }
 
 export function ensureTrailingSlash(url: string): string {
-  return url.replace(/\/?$/, '/'); // TODO #12070 #12071 add tests for this one
+  return url.replace(/\/?$/, '/'); // TODO #12070 adds slash at the front when re2 is used
 }
 
 export function trimTrailingSlash(url: string): string {
-  return url.replace(regEx(/\/+$/), ''); // TODO #12071
+  return url.replace(regEx(/\/+$/), '');
 }
 
 export function resolveBaseUrl(baseUrl: string, input: string | URL): string {
diff --git a/lib/versioning/hashicorp/index.ts b/lib/versioning/hashicorp/index.ts
index 79749443b0..62c1e99f47 100644
--- a/lib/versioning/hashicorp/index.ts
+++ b/lib/versioning/hashicorp/index.ts
@@ -49,14 +49,14 @@ function getNewValue({
         replaceValue = `$<prefix>${npm.getMinor(newVersion)}$<suffix>`;
       }
       return currentValue.replace(
-        /(?<prefix>~>\s*0\.)\d+(?<suffix>.*)$/, // TODO #12070
+        regEx(`(?<prefix>~>\\s*0\\.)\\d+(?<suffix>.*)$`), // TODO #12071
         replaceValue
       );
     }
     // handle special ~> 1.2 case
     if (regEx(/(~>\s*)\d+\.\d+$/).test(currentValue)) {
       return currentValue.replace(
-        /(?<prefix>~>\s*)\d+\.\d+$/, // TODO #12070
+        regEx(`(?<prefix>~>\\s*)\\d+\\.\\d+$`), // TODO #12071
         `$<prefix>${npm.getMajor(newVersion)}.0`
       );
     }
diff --git a/lib/versioning/rez/pattern.ts b/lib/versioning/rez/pattern.ts
index d2d7c98e46..432da27040 100644
--- a/lib/versioning/rez/pattern.ts
+++ b/lib/versioning/rez/pattern.ts
@@ -76,17 +76,17 @@ 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( // TODO #12070
+export const lowerBound = new RegExp( // TODO #12872 named backreference
   `^(?<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( // TODO #12070
+export const upperBound = new RegExp( // TODO #12872  lookahead
   `^(?<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( // TODO #12070
+export const ascendingRange = new RegExp( // TODO #12872  named backreference
   `^(?<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( // TODO #12070
+export const descendingRange = new RegExp( // TODO #12872  named backreference
   `^(?<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/ruby/index.ts b/lib/versioning/ruby/index.ts
index 929e267ae9..a1e73b7617 100644
--- a/lib/versioning/ruby/index.ts
+++ b/lib/versioning/ruby/index.ts
@@ -129,11 +129,14 @@ const getNewValue = ({
       .split(',')
       .map(
         (element) =>
-          element.replace(/^(?<whitespace>\s*)/, `$<whitespace>${delimiter}`) // TODO #12071 #12070
+          element.replace(
+            regEx(`^(?<whitespace>\\s*)`),
+            `$<whitespace>${delimiter}`
+          ) // TODO #12071
       )
       .map(
         (element) =>
-          element.replace(/(?<whitespace>\s*)$/, `${delimiter}$<whitespace>`) // TODO #12071 #12070
+          element.replace(/(?<whitespace>\s*)$/, `${delimiter}$<whitespace>`) // TODO #12071 #12070 adds ' at front when re2 is used
       )
       .join(',');
   }
-- 
GitLab