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-codec.git


The following commit(s) were added to refs/heads/master by this push:
     new 7bc15f1c Reject oversized Beider-Morse input before language guessing
7bc15f1c is described below

commit 7bc15f1cb49ec55919fba5b6a687a7290d330846
Author: Gary Gregory <[email protected]>
AuthorDate: Fri Sep 18 14:31:03 2026 -0700

    Reject oversized Beider-Morse input before language guessing
    
    Check maxInputLength before language guessing in
    PhoneticEngine.encode(String), avoiding unnecessary processing of inputs
    that will be rejected.
    
    Add regression tests for default, custom, and zero limits through the
    engine and encoder wrapper. Update the null-input test documentation.
---
 src/changes/changes.xml                            |  1 +
 .../commons/codec/language/bm/PhoneticEngine.java  |  5 +++++
 .../language/bm/PhoneticEngineBuilderTest.java     |  3 +--
 .../codec/language/bm/PhoneticEngineTest.java      | 26 ++++++++++++++++++++++
 4 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index b35320e7..a78da67f 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -63,6 +63,7 @@ The <action> type attribute can be add,update,fix,remove.
       <action type="fix" dev="ggregory" due-to="Gary Gregory">Hex decoding now 
accepts only ASCII hexadecimal characters (0-9, A-F, a-f). Previously accepted 
non-ASCII Unicode digits and fullwidth letters now cause DecoderException, 
including when supplied as UTF-8 bytes or ByteBuffers.</action>
       <action type="fix" dev="ggregory" due-to="Gary Gregory">Restrict Hex 
decoding to ASCII hexadecimal characters.</action>
       <action type="fix" dev="ggregory" due-to="Gary Gregory">Validate 
BinaryCodec input while preserving leading-bit truncation.</action>
+      <action type="fix" dev="ggregory" due-to="Gary Gregory">Reject oversized 
Beider-Morse input before language guessing.</action>
       <!-- ADD -->
       <action type="add" dev="ggregory" due-to="Gary Gregory">Add and use 
PhoneticEngine.Builder and deprecate old constructors.</action>
       <action type="add" dev="ggregory" due-to="Gary Gregory">Add 
BeiderMorseEncoder.Builder and deprecate old constructor.</action>
diff --git 
a/src/main/java/org/apache/commons/codec/language/bm/PhoneticEngine.java 
b/src/main/java/org/apache/commons/codec/language/bm/PhoneticEngine.java
index e4ca8a75..dc355eb3 100644
--- a/src/main/java/org/apache/commons/codec/language/bm/PhoneticEngine.java
+++ b/src/main/java/org/apache/commons/codec/language/bm/PhoneticEngine.java
@@ -476,6 +476,11 @@ public class PhoneticEngine {
      * @throws IllegalArgumentException if the input is longer than the 
maximum allowed length.
      */
     public String encode(final String input) {
+        // enforce the input length limit before language guessing runs over 
the input,
+        // so over-limit input cannot buy a full multi-pass scan before the 
guard fires
+        if (input.length() > maxInputLength) {
+            throw new IllegalArgumentException("Input is greater than 
maxInputLength (" + maxInputLength + ").");
+        }
         return encode(input, lang.guessLanguages(input));
     }
 
diff --git 
a/src/test/java/org/apache/commons/codec/language/bm/PhoneticEngineBuilderTest.java
 
b/src/test/java/org/apache/commons/codec/language/bm/PhoneticEngineBuilderTest.java
index ee80d081..23d52c77 100644
--- 
a/src/test/java/org/apache/commons/codec/language/bm/PhoneticEngineBuilderTest.java
+++ 
b/src/test/java/org/apache/commons/codec/language/bm/PhoneticEngineBuilderTest.java
@@ -289,12 +289,11 @@ class PhoneticEngineBuilderTest {
 
     /**
      * Tests {@link PhoneticEngine.Builder#setMaxInputLength(int)} with a 
limit of 0:
-     * null input bypasses the length check but causes a {@link 
NullPointerException} downstream.
+     * null input causes a {@link NullPointerException}.
      */
     @Test
     void testSetMaxInputLengthZeroNullInputThrowsNpe() {
         final PhoneticEngine engine = 
PhoneticEngine.builder().setMaxInputLength(0).get();
-        // null is not blocked by the length check, but encoding null causes 
NPE
         assertThrows(NullPointerException.class, () -> engine.encode(null));
     }
 
diff --git 
a/src/test/java/org/apache/commons/codec/language/bm/PhoneticEngineTest.java 
b/src/test/java/org/apache/commons/codec/language/bm/PhoneticEngineTest.java
index edcb87ed..a49628be 100644
--- a/src/test/java/org/apache/commons/codec/language/bm/PhoneticEngineTest.java
+++ b/src/test/java/org/apache/commons/codec/language/bm/PhoneticEngineTest.java
@@ -18,14 +18,18 @@
 package org.apache.commons.codec.language.bm;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.lang.reflect.Field;
+import java.util.Arrays;
 import java.util.regex.Pattern;
 import java.util.stream.Stream;
 
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
 
 /**
  * Tests PhoneticEngine.
@@ -96,4 +100,26 @@ class PhoneticEngineTest {
 
         assertEquals(engine.encode(input), phoneticExpected);
     }
+
+    @ParameterizedTest
+    @ValueSource(ints = { -1, 0, 10, 666 })
+    void testRejectsOversizedInputBeforeLanguageGuessing(final int 
configuredLimit) throws Exception {
+        final PhoneticEngine engine = 
PhoneticEngine.builder().setMaxInputLength(configuredLimit).get();
+        final int limit = configuredLimit < 0 ? 666 : configuredLimit;
+        final char[] chars = new char[limit + 1];
+        Arrays.fill(chars, 'a');
+        final String input = new String(chars);
+
+        // Make language guessing fail if reached, without changing the shared 
Lang instance or relying on timing.
+        final Field langField = PhoneticEngine.class.getDeclaredField("lang");
+        langField.setAccessible(true);
+        langField.set(engine, null);
+        assertThrows(NullPointerException.class, () -> 
engine.encode(input.substring(1)));
+
+        final String message = "Input is greater than maxInputLength (" + 
limit + ").";
+        assertEquals(message, assertThrows(IllegalArgumentException.class, () 
-> engine.encode(input)).getMessage());
+        assertEquals(message, assertThrows(IllegalArgumentException.class, () 
-> engine.encode(input, Languages.ANY_LANGUAGE)).getMessage());
+        final BeiderMorseEncoder encoder = 
BeiderMorseEncoder.builder().setPhoneticEngine(engine).get();
+        assertEquals(message, assertThrows(IllegalArgumentException.class, () 
-> encoder.encode(input)).getMessage());
+    }
 }

Reply via email to