This is an automated email from the ASF dual-hosted git repository.

kwin pushed a commit to branch 
bugfix/prevent-infinite-loop-in-ManifestResourceTransformer
in repository https://gitbox.apache.org/repos/asf/maven-shade-plugin.git

commit c10aa07d013e9e1a678146ab7b151ee620274d54
Author: Konrad Windszus <[email protected]>
AuthorDate: Wed Mar 4 20:14:46 2026 +0100

    Prevent infinite loop in ManifestResourceTransformer.relocate
    
    Introduce Relocator.relocateAllClasses() to replace all patterns in a
    given string instead of only the first one and use that.
    Otherwise recursively using Relocator.relocateClass() on already
    relocated string over and over again leads to endless loop when pattern
    is a prefix of the shaded pattern.
    
    This closes #478
---
 .../maven/plugins/shade/relocation/Relocator.java  | 12 +++-
 .../plugins/shade/relocation/SimpleRelocator.java  |  9 ++-
 .../resource/ManifestResourceTransformer.java      |  6 +-
 .../shade/relocation/SimpleRelocatorTest.java      | 15 +++++
 .../resource/ManifestResourceTransformerTest.java  | 77 +++++++++++++++-------
 5 files changed, 88 insertions(+), 31 deletions(-)

diff --git 
a/src/main/java/org/apache/maven/plugins/shade/relocation/Relocator.java 
b/src/main/java/org/apache/maven/plugins/shade/relocation/Relocator.java
index d3027c4..be9ac6c 100644
--- a/src/main/java/org/apache/maven/plugins/shade/relocation/Relocator.java
+++ b/src/main/java/org/apache/maven/plugins/shade/relocation/Relocator.java
@@ -28,7 +28,17 @@ public interface Relocator {
 
     boolean canRelocateClass(String clazz);
 
-    String relocateClass(String clazz);
+    /**
+     * Replace the class first pattern match in the given string
+     * @see #relocateAllClasses(String)
+     */
+    String relocateClass(String input);
 
     String applyToSourceContent(String sourceContent);
+
+    /**
+     * Replace all class pattern matches in the given input.
+     * @see #relocateClass(String)
+     */
+    String relocateAllClasses(String input);
 }
diff --git 
a/src/main/java/org/apache/maven/plugins/shade/relocation/SimpleRelocator.java 
b/src/main/java/org/apache/maven/plugins/shade/relocation/SimpleRelocator.java
index 9d49f77..b801fd0 100644
--- 
a/src/main/java/org/apache/maven/plugins/shade/relocation/SimpleRelocator.java
+++ 
b/src/main/java/org/apache/maven/plugins/shade/relocation/SimpleRelocator.java
@@ -208,8 +208,13 @@ public class SimpleRelocator implements Relocator {
     }
 
     @Override
-    public String relocateClass(String clazz) {
-        return rawString ? clazz : clazz.replaceFirst(pattern, shadedPattern);
+    public String relocateClass(String input) {
+        return rawString ? input : input.replaceFirst(pattern, shadedPattern);
+    }
+
+    @Override
+    public String relocateAllClasses(String input) {
+        return rawString ? input : input.replaceAll(pattern, shadedPattern);
     }
 
     @Override
diff --git 
a/src/main/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformer.java
 
b/src/main/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformer.java
index 9ef598b..b9d63b3 100644
--- 
a/src/main/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformer.java
+++ 
b/src/main/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformer.java
@@ -152,11 +152,7 @@ public class ManifestResourceTransformer extends 
AbstractCompatibilityTransforme
     private String relocate(String originalValue, List<Relocator> relocators) {
         String newValue = originalValue;
         for (Relocator relocator : relocators) {
-            String value;
-            do {
-                value = newValue;
-                newValue = relocator.relocateClass(value);
-            } while (!value.equals(newValue));
+            newValue = relocator.relocateAllClasses(newValue);
         }
         return newValue;
     }
diff --git 
a/src/test/java/org/apache/maven/plugins/shade/relocation/SimpleRelocatorTest.java
 
b/src/test/java/org/apache/maven/plugins/shade/relocation/SimpleRelocatorTest.java
index 9f7c29d..44afee3 100644
--- 
a/src/test/java/org/apache/maven/plugins/shade/relocation/SimpleRelocatorTest.java
+++ 
b/src/test/java/org/apache/maven/plugins/shade/relocation/SimpleRelocatorTest.java
@@ -157,6 +157,21 @@ public class SimpleRelocatorTest {
         assertEquals("private.stuff.bar.Class", 
relocator.relocateClass("org.foo.bar.Class"));
     }
 
+    @Test
+    public void testRelocateAllClasses() {
+        SimpleRelocator relocator;
+
+        relocator = new SimpleRelocator("org.foo", null, null, null);
+        assertEquals(
+                "hidden.org.foo.bar.Class, hidden.hidden.org.foo.bar.Class and 
hidden.org.foo.bar.Class",
+                relocator.relocateAllClasses("org.foo.bar.Class, 
hidden.org.foo.bar.Class and org.foo.bar.Class"));
+
+        relocator = new SimpleRelocator("org.foo", "private.stuff", null, 
null);
+        assertEquals(
+                "private.stuff.bar.Class, private.stuff.bar.Class and 
private.stuff.bar.Class",
+                relocator.relocateAllClasses("org.foo.bar.Class, 
private.stuff.bar.Class and org.foo.bar.Class"));
+    }
+
     @Test
     public void testRelocateRawString() {
         SimpleRelocator relocator;
diff --git 
a/src/test/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformerTest.java
 
b/src/test/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformerTest.java
index ed14ed2..59bb33f 100644
--- 
a/src/test/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformerTest.java
+++ 
b/src/test/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformerTest.java
@@ -48,6 +48,47 @@ public class ManifestResourceTransformerTest {
 
     @Test
     public void rewriteDefaultAttributes() throws Exception {
+        final Manifest manifest = createTestManifest();
+
+        List<Relocator> relocators = Collections.<Relocator>singletonList(new 
SimpleRelocator(
+                "javax", "jakarta", Collections.<String>emptyList(), 
Collections.<String>emptyList()));
+
+        final ByteArrayOutputStream out = transform(manifest, relocators);
+
+        try (JarInputStream jis = new JarInputStream(new 
ByteArrayInputStream(out.toByteArray()))) {
+            Attributes attrs = jis.getManifest().getMainAttributes();
+            assertEquals(
+                    
"jakarta.decorator;version=\"2.0\";uses:=\"jakarta.enterprise.inject\","
+                            + 
"jakarta.enterprise.context;version=\"2.0\";uses:=\"jakarta.enterprise.util,"
+                            + "jakarta.inject\"",
+                    attrs.getValue("Export-Package"));
+            
assertEquals("jakarta.el,jakarta.enterprise.context;version=\"[2.0,3)\"", 
attrs.getValue("Import-Package"));
+            assertEquals(
+                    "osgi.contract;osgi.contract=JavaCDI;" + 
"uses:=\"jakarta.enterprise.context,"
+                            + 
"jakarta.enterprise.context.spi,jakarta.enterprise.context.control,"
+                            + 
"jakarta.enterprise.util,jakarta.enterprise.inject,jakarta.enterprise.inject.spi,"
+                            + 
"jakarta.enterprise.inject.spi.configurator,jakarta.enterprise.inject.literal,"
+                            + 
"jakarta.enterprise.inject.se,jakarta.enterprise.event,"
+                            + 
"jakarta.decorator\";version:List<Version>=\"2.0,1.2,1.1,1.0\"",
+                    attrs.getValue("Provide-Capability"));
+            assertEquals(
+                    "osgi.serviceloader;"
+                            + 
"filter:=\"(osgi.serviceloader=jakarta.enterprise.inject.se.SeContainerInitializer)\";"
+                            + "cardinality:=multiple,osgi.serviceloader;"
+                            + 
"filter:=\"(osgi.serviceloader=jakarta.enterprise.inject.spi.CDIProvider)\";"
+                            + "cardinality:=multiple,osgi.extender;"
+                            + 
"filter:=\"(osgi.extender=osgi.serviceloader.processor)\","
+                            + 
"osgi.contract;osgi.contract=JavaEL;filter:=\"(&(osgi.contract=JavaEL)(version=2.2.0))\","
+                            + "osgi.contract;osgi.contract=JavaInterceptor;"
+                            + 
"filter:=\"(&(osgi.contract=JavaInterceptor)(version=1.2.0))\","
+                            + "osgi.contract;osgi.contract=JavaInject;"
+                            + 
"filter:=\"(&(osgi.contract=JavaInject)(version=1.0.0))\","
+                            + 
"osgi.ee;filter:=\"(&(osgi.ee=JavaSE)(version=1.8))\"",
+                    attrs.getValue("Require-Capability"));
+        }
+    }
+
+    protected Manifest createTestManifest() {
         final Manifest manifest = new Manifest();
         final Attributes attributes = manifest.getMainAttributes();
         attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
@@ -79,41 +120,31 @@ public class ManifestResourceTransformerTest {
                         + "osgi.contract;osgi.contract=JavaInject;"
                         + 
"filter:=\"(&(osgi.contract=JavaInject)(version=1.0.0))\","
                         + 
"osgi.ee;filter:=\"(&(osgi.ee=JavaSE)(version=1.8))\"");
+        return manifest;
+    }
+
+    @Test()
+    public void rewriteDefaultAttributesWithSameSuffix() throws Exception {
+        final Manifest manifest = createTestManifest();
 
         List<Relocator> relocators = Collections.<Relocator>singletonList(new 
SimpleRelocator(
-                "javax", "jakarta", Collections.<String>emptyList(), 
Collections.<String>emptyList()));
+                "javax", "shaded.javax", Collections.<String>emptyList(), 
Collections.<String>emptyList()));
 
         final ByteArrayOutputStream out = transform(manifest, relocators);
 
         try (JarInputStream jis = new JarInputStream(new 
ByteArrayInputStream(out.toByteArray()))) {
             Attributes attrs = jis.getManifest().getMainAttributes();
             assertEquals(
-                    
"jakarta.decorator;version=\"2.0\";uses:=\"jakarta.enterprise.inject\","
-                            + 
"jakarta.enterprise.context;version=\"2.0\";uses:=\"jakarta.enterprise.util,"
-                            + "jakarta.inject\"",
+                    
"shaded.javax.decorator;version=\"2.0\";uses:=\"shaded.javax.enterprise.inject\",shaded.javax.enterprise.context;version=\"2.0\";uses:=\"shaded.javax.enterprise.util,shaded.javax.inject\"",
                     attrs.getValue("Export-Package"));
-            
assertEquals("jakarta.el,jakarta.enterprise.context;version=\"[2.0,3)\"", 
attrs.getValue("Import-Package"));
             assertEquals(
-                    "osgi.contract;osgi.contract=JavaCDI;" + 
"uses:=\"jakarta.enterprise.context,"
-                            + 
"jakarta.enterprise.context.spi,jakarta.enterprise.context.control,"
-                            + 
"jakarta.enterprise.util,jakarta.enterprise.inject,jakarta.enterprise.inject.spi,"
-                            + 
"jakarta.enterprise.inject.spi.configurator,jakarta.enterprise.inject.literal,"
-                            + 
"jakarta.enterprise.inject.se,jakarta.enterprise.event,"
-                            + 
"jakarta.decorator\";version:List<Version>=\"2.0,1.2,1.1,1.0\"",
+                    
"shaded.javax.el,shaded.javax.enterprise.context;version=\"[2.0,3)\"",
+                    attrs.getValue("Import-Package"));
+            assertEquals(
+                    
"osgi.contract;osgi.contract=JavaCDI;uses:=\"shaded.javax.enterprise.context,shaded.javax.enterprise.context.spi,shaded.javax.enterprise.context.control,shaded.javax.enterprise.util,shaded.javax.enterprise.inject,shaded.javax.enterprise.inject.spi,shaded.javax.enterprise.inject.spi.configurator,shaded.javax.enterprise.inject.literal,shaded.javax.enterprise.inject.se,shaded.javax.enterprise.event,shaded.javax.decorator\";version:List<Version>=\"2.0,1.2,1.1,1.0\"",
                     attrs.getValue("Provide-Capability"));
             assertEquals(
-                    "osgi.serviceloader;"
-                            + 
"filter:=\"(osgi.serviceloader=jakarta.enterprise.inject.se.SeContainerInitializer)\";"
-                            + "cardinality:=multiple,osgi.serviceloader;"
-                            + 
"filter:=\"(osgi.serviceloader=jakarta.enterprise.inject.spi.CDIProvider)\";"
-                            + "cardinality:=multiple,osgi.extender;"
-                            + 
"filter:=\"(osgi.extender=osgi.serviceloader.processor)\","
-                            + 
"osgi.contract;osgi.contract=JavaEL;filter:=\"(&(osgi.contract=JavaEL)(version=2.2.0))\","
-                            + "osgi.contract;osgi.contract=JavaInterceptor;"
-                            + 
"filter:=\"(&(osgi.contract=JavaInterceptor)(version=1.2.0))\","
-                            + "osgi.contract;osgi.contract=JavaInject;"
-                            + 
"filter:=\"(&(osgi.contract=JavaInject)(version=1.0.0))\","
-                            + 
"osgi.ee;filter:=\"(&(osgi.ee=JavaSE)(version=1.8))\"",
+                    
"osgi.serviceloader;filter:=\"(osgi.serviceloader=shaded.javax.enterprise.inject.se.SeContainerInitializer)\";cardinality:=multiple,osgi.serviceloader;filter:=\"(osgi.serviceloader=shaded.javax.enterprise.inject.spi.CDIProvider)\";cardinality:=multiple,osgi.extender;filter:=\"(osgi.extender=osgi.serviceloader.processor)\",osgi.contract;osgi.contract=JavaEL;filter:=\"(&(osgi.contract=JavaEL)(version=2.2.0))\",osgi.contract;osgi.contract=JavaInterceptor;filter:=\"(&(osg
 [...]
                     attrs.getValue("Require-Capability"));
         }
     }

Reply via email to