From 6b42b45eb4ddaee5ceb371577a9a09c18b2520ae Mon Sep 17 00:00:00 2001
From: Sergei Zharinov <zharinov@users.noreply.github.com>
Date: Sat, 29 Oct 2022 07:58:43 +0300
Subject: [PATCH] fix(maven): Fix recursive props check (#18644)

---
 .../multiple_usages_props.pom.xml             | 18 +++++++++++++++
 lib/modules/manager/maven/extract.ts          | 23 +++++++++++--------
 lib/modules/manager/maven/index.spec.ts       | 12 ++++++++++
 3 files changed, 44 insertions(+), 9 deletions(-)
 create mode 100644 lib/modules/manager/maven/__fixtures__/multiple_usages_props.pom.xml

diff --git a/lib/modules/manager/maven/__fixtures__/multiple_usages_props.pom.xml b/lib/modules/manager/maven/__fixtures__/multiple_usages_props.pom.xml
new file mode 100644
index 0000000000..2f58cfc2bf
--- /dev/null
+++ b/lib/modules/manager/maven/__fixtures__/multiple_usages_props.pom.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.example</groupId>
+    <artifactId>ExamplePomFile</artifactId>
+
+    <properties>
+        <foo>1.2.3</foo>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.lucene</groupId>
+            <artifactId>lucene-core-${foo}.${foo}</artifactId>
+            <version>${foo}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/lib/modules/manager/maven/extract.ts b/lib/modules/manager/maven/extract.ts
index f7e9575696..4a5b861904 100644
--- a/lib/modules/manager/maven/extract.ts
+++ b/lib/modules/manager/maven/extract.ts
@@ -33,7 +33,7 @@ export function parsePom(raw: string): XmlDocument | null {
 }
 
 function containsPlaceholder(str: string | null | undefined): boolean {
-  return !!str && regEx(/\${.*?}/g).test(str);
+  return !!str && regEx(/\${[^}]*?}/).test(str);
 }
 
 function depFromNode(
@@ -126,7 +126,7 @@ function applyProps(
 ): PackageDependency<Record<string, any>> {
   let result = dep;
   let anyChange = false;
-  const alreadySeenProps: string[] = [];
+  const alreadySeenProps: Set<string> = new Set();
 
   do {
     const [returnedResult, returnedAnyChange, fatal] = applyPropsInternal(
@@ -156,22 +156,24 @@ function applyPropsInternal(
   dep: PackageDependency<Record<string, any>>,
   depPackageFile: string,
   props: MavenProp,
-  alreadySeenProps: string[]
+  previouslySeenProps: Set<string>
 ): [PackageDependency<Record<string, any>>, boolean, boolean] {
   let anyChange = false;
   let fatal = false;
 
+  const seenProps: Set<string> = new Set();
+
   const replaceAll = (str: string): string =>
-    str.replace(regEx(/\${.*?}/g), (substr) => {
+    str.replace(regEx(/\${[^}]*?}/g), (substr) => {
       const propKey = substr.slice(2, -1).trim();
       // TODO: wrong types here, props is already `MavenProp`
       const propValue = (props as any)[propKey] as MavenProp;
       if (propValue) {
         anyChange = true;
-        if (alreadySeenProps.find((it) => it === propKey)) {
+        if (previouslySeenProps.has(propKey)) {
           fatal = true;
         } else {
-          alreadySeenProps.push(propKey);
+          seenProps.add(propKey);
         }
         return propValue.val;
       }
@@ -185,7 +187,7 @@ function applyPropsInternal(
   let propSource = dep.propSource;
   let groupName: string | null = null;
   const currentValue = dep.currentValue!.replace(
-    regEx(/^\${.*?}$/),
+    regEx(/^\${[^}]*?}$/),
     (substr) => {
       const propKey = substr.slice(2, -1).trim();
       // TODO: wrong types here, props is already `MavenProp`
@@ -197,10 +199,10 @@ function applyPropsInternal(
         fileReplacePosition = propValue.fileReplacePosition;
         propSource = propValue.packageFile ?? undefined;
         anyChange = true;
-        if (alreadySeenProps.find((it) => it === propKey)) {
+        if (previouslySeenProps.has(propKey)) {
           fatal = true;
         } else {
-          alreadySeenProps.push(propKey);
+          seenProps.add(propKey);
         }
         return propValue.val;
       }
@@ -225,6 +227,9 @@ function applyPropsInternal(
     result.editFile = propSource;
   }
 
+  for (const prop of seenProps) {
+    previouslySeenProps.add(prop);
+  }
   return [result, anyChange, fatal];
 }
 
diff --git a/lib/modules/manager/maven/index.spec.ts b/lib/modules/manager/maven/index.spec.ts
index a614327fdd..6048157636 100644
--- a/lib/modules/manager/maven/index.spec.ts
+++ b/lib/modules/manager/maven/index.spec.ts
@@ -189,6 +189,18 @@ describe('modules/manager/maven/index', () => {
       ]);
     });
 
+    it('should apply props multiple times', () => {
+      const [{ deps }] = resolveParents([
+        extractPackage(Fixtures.get('multiple_usages_props.pom.xml'))!,
+      ]);
+      expect(deps).toMatchObject([
+        {
+          depName: 'org.apache.lucene:lucene-core-1.2.3.1.2.3',
+          currentValue: '1.2.3',
+        },
+      ]);
+    });
+
     it('should detect props infinitely recursing props', () => {
       const [{ deps }] = resolveParents([
         extractPackage(Fixtures.get('infinite_recursive_props.pom.xml'))!,
-- 
GitLab