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

garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git


The following commit(s) were added to refs/heads/master by this push:
     new 184c9e832 WordUtils.wrap() zero-width wrapOn regex match causes 
infinite loop. (#1649)
184c9e832 is described below

commit 184c9e83211a0552698a934d45d3ebca2c789881
Author: Gary Gregory <[email protected]>
AuthorDate: Sun May 17 11:29:52 2026 -0400

    WordUtils.wrap() zero-width wrapOn regex match causes infinite loop. (#1649)
---
 src/main/java/org/apache/commons/lang3/text/WordUtils.java     | 8 ++------
 src/test/java/org/apache/commons/lang3/text/WordUtilsTest.java | 7 +++++++
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/src/main/java/org/apache/commons/lang3/text/WordUtils.java 
b/src/main/java/org/apache/commons/lang3/text/WordUtils.java
index ea6c2ad54..ae59a34e3 100644
--- a/src/main/java/org/apache/commons/lang3/text/WordUtils.java
+++ b/src/main/java/org/apache/commons/lang3/text/WordUtils.java
@@ -652,21 +652,19 @@ public static String wrap(final String str, int 
wrapLength, String newLineStr, f
                 str.substring(offset, Math.min((int) 
Math.min(Integer.MAX_VALUE, offset + wrapLength + 1L), inputLineLength)));
             if (matcher.find()) {
                 if (matcher.start() == 0) {
-                    offset += matcher.end();
+                    // If the match is zero-width, advance by at least 1 to 
avoid infinite loop.
+                    offset += matcher.end() > 0 ? matcher.end() : 1;
                     continue;
                 }
                 spaceToWrapAt = matcher.start() + offset;
             }
-
             // only last line without leading spaces is left
             if (inputLineLength - offset <= wrapLength) {
                 break;
             }
-
             while (matcher.find()) {
                 spaceToWrapAt = matcher.start() + offset;
             }
-
             if (spaceToWrapAt >= offset) {
                 // normal case
                 wrappedLine.append(str, offset, spaceToWrapAt);
@@ -696,10 +694,8 @@ public static String wrap(final String str, int 
wrapLength, String newLineStr, f
                 }
             }
         }
-
         // Whatever is left in line is short enough to just pass through
         wrappedLine.append(str, offset, str.length());
-
         return wrappedLine.toString();
     }
 
diff --git a/src/test/java/org/apache/commons/lang3/text/WordUtilsTest.java 
b/src/test/java/org/apache/commons/lang3/text/WordUtilsTest.java
index e59a60567..e14b14cbf 100644
--- a/src/test/java/org/apache/commons/lang3/text/WordUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/text/WordUtilsTest.java
@@ -20,10 +20,12 @@
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTimeout;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Modifier;
+import java.time.Duration;
 
 import org.apache.commons.lang3.AbstractLangTest;
 import org.junit.jupiter.api.Test;
@@ -429,4 +431,9 @@ void testWrap_StringIntStringBooleanString() {
         expected = "flammableinflam\nmable";
         assertEquals(expected, WordUtils.wrap(input, 15, "\n", true, "/"));
     }
+
+    @Test
+    void testZeroWidthWrapOnRegex() {
+        assertTimeout(Duration.ofSeconds(2), () -> 
assertNotNull(WordUtils.wrap("abcdef", 3, "\n", false, "(?=a)")));
+    }
 }

Reply via email to