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


The following commit(s) were added to refs/heads/master by this push:
     new 1b86e98a Reject trailing characters in DecimalLocaleConverter.parse 
(#399)
1b86e98a is described below

commit 1b86e98afb0e8f30dc7c2123194a228ff5ff8e6e
Author: Dexter.k <[email protected]>
AuthorDate: Sun Jun 21 18:33:55 2026 +0000

    Reject trailing characters in DecimalLocaleConverter.parse (#399)
    
    * reject trailing characters in DecimalLocaleConverter.parse
    
    DecimalFormat.parse(String) stops at the first unparsable character and
    discards the rest, so numeric locale conversion accepted trailing input.
    Parse with a ParsePosition and require the whole string to be consumed,
    matching DateLocaleConverter and NumberConverter.
    
    * update locale converter tests for strict parse
    
    The (B) cases asserted DecimalFormat's partial parse of wrong-format input; 
with the whole string now required, those inputs return the converter default. 
Updated the expected values and now-stale comments.
---
 .../locale/converters/DecimalLocaleConverter.java  |  9 ++-
 .../converters/BigDecimalLocaleConverterTest.java  |  8 +--
 .../converters/BigIntegerLocaleConverterTest.java  |  8 +--
 .../converters/DecimalLocaleConverterTest.java     | 67 ++++++++++++++++++++++
 .../converters/DoubleLocaleConverterTest.java      |  8 +--
 .../converters/FloatLocaleConverterTest.java       |  8 +--
 .../converters/IntegerLocaleConverterTest.java     |  8 +--
 .../converters/LongLocaleConverterTest.java        |  8 +--
 .../converters/ShortLocaleConverterTest.java       |  8 +--
 9 files changed, 96 insertions(+), 36 deletions(-)

diff --git 
a/src/main/java/org/apache/commons/beanutils2/locale/converters/DecimalLocaleConverter.java
 
b/src/main/java/org/apache/commons/beanutils2/locale/converters/DecimalLocaleConverter.java
index ab60b7d1..bf65ff51 100644
--- 
a/src/main/java/org/apache/commons/beanutils2/locale/converters/DecimalLocaleConverter.java
+++ 
b/src/main/java/org/apache/commons/beanutils2/locale/converters/DecimalLocaleConverter.java
@@ -20,6 +20,7 @@ package org.apache.commons.beanutils2.locale.converters;
 import java.text.DecimalFormat;
 import java.text.NumberFormat;
 import java.text.ParseException;
+import java.text.ParsePosition;
 import java.util.Locale;
 
 import org.apache.commons.beanutils2.ConversionException;
@@ -129,6 +130,12 @@ public class DecimalLocaleConverter<T extends Number> 
extends BaseLocaleConverte
             LOG.debug("No pattern provided, using default.");
         }
 
-        return (T) formatter.parse((String) value);
+        final String strValue = (String) value;
+        final ParsePosition pos = new ParsePosition(0);
+        final Number parsed = formatter.parse(strValue, pos);
+        if (parsed == null || pos.getErrorIndex() >= 0 || pos.getIndex() != 
strValue.length()) {
+            throw new ParseException("Error parsing '" + strValue + "' as a 
number", pos.getIndex());
+        }
+        return (T) parsed;
     }
 }
diff --git 
a/src/test/java/org/apache/commons/beanutils2/converters/BigDecimalLocaleConverterTest.java
 
b/src/test/java/org/apache/commons/beanutils2/converters/BigDecimalLocaleConverterTest.java
index 1012e4db..ca23ce0a 100644
--- 
a/src/test/java/org/apache/commons/beanutils2/converters/BigDecimalLocaleConverterTest.java
+++ 
b/src/test/java/org/apache/commons/beanutils2/converters/BigDecimalLocaleConverterTest.java
@@ -194,11 +194,9 @@ class BigDecimalLocaleConverterTest extends 
AbstractLocaleConverterTest<BigDecim
         convertInvalid(converter, "(A)", defaultValue);
         convertNull(converter, "(A)", defaultValue);
 
-        // Convert value in the wrong format - maybe you would expect it to 
throw an
-        // exception and return the default - it doesn't, DecimalFormat parses 
it
-        // quite happily turning "1,234.56" into "1.234"
-        // I guess this is one of the limitations of DecimalFormat
-        convertValueNoPattern(converter, "(B)", defaultDecimalValue, new 
BigDecimal("1.234"));
+        // Convert value in the wrong format - the trailing characters left 
after the
+        // partial parse are now rejected, so the converter returns the 
default.
+        convertValueNoPattern(converter, "(B)", defaultDecimalValue, 
defaultValue);
 
         // Convert with non-localized pattern - this causes an exception in 
parse()
         // but it gets swallowed in convert() method and returns default.
diff --git 
a/src/test/java/org/apache/commons/beanutils2/converters/BigIntegerLocaleConverterTest.java
 
b/src/test/java/org/apache/commons/beanutils2/converters/BigIntegerLocaleConverterTest.java
index c45cff49..62a1bf27 100644
--- 
a/src/test/java/org/apache/commons/beanutils2/converters/BigIntegerLocaleConverterTest.java
+++ 
b/src/test/java/org/apache/commons/beanutils2/converters/BigIntegerLocaleConverterTest.java
@@ -203,12 +203,10 @@ class BigIntegerLocaleConverterTest extends 
AbstractLocaleConverterTest<BigInteg
         convertValueNoPattern(converter, "(B)", defaultIntegerValue, new 
BigInteger("1"));
 
         // 
**************************************************************************
-        // Convert with non-localized pattern - unlike the equivalent 
BigDecimal Test Case
-        // it doesn't causes an exception in parse() - DecimalFormat parses it
-        // quite happily turning "1,234" into "1"
-        // Again this is one of the limitations of DecimalFormat
+        // Convert with non-localized pattern - the trailing characters left 
after the
+        // partial parse are now rejected, so the converter returns the 
default.
         // 
**************************************************************************
-        convertValueWithPattern(converter, "(B)", localizedIntegerValue, 
defaultIntegerPattern, new BigInteger("1"));
+        convertValueWithPattern(converter, "(B)", localizedIntegerValue, 
defaultIntegerPattern, defaultValue);
 
         // 
**************************************************************************
         // Convert with specified type
diff --git 
a/src/test/java/org/apache/commons/beanutils2/converters/DecimalLocaleConverterTest.java
 
b/src/test/java/org/apache/commons/beanutils2/converters/DecimalLocaleConverterTest.java
new file mode 100644
index 00000000..ecfd8794
--- /dev/null
+++ 
b/src/test/java/org/apache/commons/beanutils2/converters/DecimalLocaleConverterTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.beanutils2.converters;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.Locale;
+
+import org.apache.commons.beanutils2.ConversionException;
+import org.apache.commons.beanutils2.locale.converters.DecimalLocaleConverter;
+import org.apache.commons.beanutils2.locale.converters.IntegerLocaleConverter;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests that {@link DecimalLocaleConverter} fully consumes its input and 
rejects trailing, unparsed characters.
+ */
+class DecimalLocaleConverterTest {
+
+    private Locale origLocale;
+
+    @BeforeEach
+    public void setUp() {
+        origLocale = Locale.getDefault();
+        Locale.setDefault(Locale.US);
+    }
+
+    @AfterEach
+    public void tearDown() {
+        Locale.setDefault(origLocale);
+    }
+
+    @Test
+    void testParseAcceptsGroupedNumber() {
+        final IntegerLocaleConverter converter = 
IntegerLocaleConverter.builder().setLocale(Locale.US).get();
+        assertEquals(Integer.valueOf(1234), converter.convert("1,234"));
+    }
+
+    @Test
+    void testParseRejectsTrailingCharacters() {
+        final IntegerLocaleConverter converter = 
IntegerLocaleConverter.builder().setLocale(Locale.US).get();
+        assertThrows(ConversionException.class, () -> 
converter.convert("123abc"));
+    }
+
+    @Test
+    void testParseRejectsTrailingExpression() {
+        final IntegerLocaleConverter converter = 
IntegerLocaleConverter.builder().setLocale(Locale.US).get();
+        assertThrows(ConversionException.class, () -> converter.convert("42 OR 
1=1"));
+    }
+}
diff --git 
a/src/test/java/org/apache/commons/beanutils2/converters/DoubleLocaleConverterTest.java
 
b/src/test/java/org/apache/commons/beanutils2/converters/DoubleLocaleConverterTest.java
index 3490ba0c..61d3e360 100644
--- 
a/src/test/java/org/apache/commons/beanutils2/converters/DoubleLocaleConverterTest.java
+++ 
b/src/test/java/org/apache/commons/beanutils2/converters/DoubleLocaleConverterTest.java
@@ -196,12 +196,10 @@ class DoubleLocaleConverterTest extends 
AbstractLocaleConverterTest<Double> {
         convertNull(converter, "(A)", defaultValue);
 
         // 
**************************************************************************
-        // Convert value in the wrong format - maybe you would expect it to 
throw an
-        // exception and return the default - it doesn't, DecimalFormat parses 
it
-        // quite happily turning "1,234.56" into "1.234"
-        // I guess this is one of the limitations of DecimalFormat
+        // Convert value in the wrong format - the trailing characters left 
after the
+        // partial parse are now rejected, so the converter returns the 
default.
         // 
**************************************************************************
-        convertValueNoPattern(converter, "(B)", defaultDecimalValue, 
Double.valueOf("1.234"));
+        convertValueNoPattern(converter, "(B)", defaultDecimalValue, 
defaultValue);
 
         // 
**************************************************************************
         // Convert with non-localized pattern - this causes an exception in 
parse()
diff --git 
a/src/test/java/org/apache/commons/beanutils2/converters/FloatLocaleConverterTest.java
 
b/src/test/java/org/apache/commons/beanutils2/converters/FloatLocaleConverterTest.java
index 4d399bf7..004b5dab 100644
--- 
a/src/test/java/org/apache/commons/beanutils2/converters/FloatLocaleConverterTest.java
+++ 
b/src/test/java/org/apache/commons/beanutils2/converters/FloatLocaleConverterTest.java
@@ -204,12 +204,10 @@ class FloatLocaleConverterTest extends 
AbstractLocaleConverterTest<Float> {
         convertNull(converter, "(A)", defaultValue);
 
         // 
**************************************************************************
-        // Convert value in the wrong format - maybe you would expect it to 
throw an
-        // exception and return the default - it doesn't, DecimalFormat parses 
it
-        // quite happily turning "1,234.56" into "1.234"
-        // I guess this is one of the limitations of DecimalFormat
+        // Convert value in the wrong format - the trailing characters left 
after the
+        // partial parse are now rejected, so the converter returns the 
default.
         // 
**************************************************************************
-        convertValueNoPattern(converter, "(B)", defaultDecimalValue, 
Float.valueOf("1.234"));
+        convertValueNoPattern(converter, "(B)", defaultDecimalValue, 
defaultValue);
 
         // 
**************************************************************************
         // Convert with non-localized pattern - this causes an exception in 
parse()
diff --git 
a/src/test/java/org/apache/commons/beanutils2/converters/IntegerLocaleConverterTest.java
 
b/src/test/java/org/apache/commons/beanutils2/converters/IntegerLocaleConverterTest.java
index e2eb131b..2d721588 100644
--- 
a/src/test/java/org/apache/commons/beanutils2/converters/IntegerLocaleConverterTest.java
+++ 
b/src/test/java/org/apache/commons/beanutils2/converters/IntegerLocaleConverterTest.java
@@ -187,12 +187,10 @@ class IntegerLocaleConverterTest extends 
AbstractLocaleConverterTest<Integer> {
         convertValueNoPattern(converter, "(B)", defaultIntegerValue, 
Integer.valueOf("1"));
 
         // 
**************************************************************************
-        // Convert with non-localized pattern - unlike the equivalent 
BigDecimal Test Case
-        // it doesn't causes an exception in parse() - DecimalFormat parses it
-        // quite happily turning "1,234" into "1"
-        // Again this is one of the limitations of DecimalFormat
+        // Convert with non-localized pattern - the trailing characters left 
after the
+        // partial parse are now rejected, so the converter returns the 
default.
         // 
**************************************************************************
-        convertValueWithPattern(converter, "(B)", localizedIntegerValue, 
defaultIntegerPattern, Integer.valueOf("1"));
+        convertValueWithPattern(converter, "(B)", localizedIntegerValue, 
defaultIntegerPattern, defaultValue);
 
         // 
**************************************************************************
         // Convert with specified type
diff --git 
a/src/test/java/org/apache/commons/beanutils2/converters/LongLocaleConverterTest.java
 
b/src/test/java/org/apache/commons/beanutils2/converters/LongLocaleConverterTest.java
index 544c70a9..0202c585 100644
--- 
a/src/test/java/org/apache/commons/beanutils2/converters/LongLocaleConverterTest.java
+++ 
b/src/test/java/org/apache/commons/beanutils2/converters/LongLocaleConverterTest.java
@@ -204,12 +204,10 @@ class LongLocaleConverterTest extends 
AbstractLocaleConverterTest<Long> {
         convertValueNoPattern(converter, "(B)", defaultIntegerValue, 
Long.valueOf("1"));
 
         // 
**************************************************************************
-        // Convert with non-localized pattern - unlike the equivalent 
BigDecimal Test Case
-        // it doesn't causes an exception in parse() - DecimalFormat parses it
-        // quite happily turning "1,234" into "1"
-        // Again this is one of the limitations of DecimalFormat
+        // Convert with non-localized pattern - the trailing characters left 
after the
+        // partial parse are now rejected, so the converter returns the 
default.
         // 
**************************************************************************
-        convertValueWithPattern(converter, "(B)", localizedIntegerValue, 
defaultIntegerPattern, Long.valueOf("1"));
+        convertValueWithPattern(converter, "(B)", localizedIntegerValue, 
defaultIntegerPattern, defaultValue);
 
         // 
**************************************************************************
         // Convert with specified type
diff --git 
a/src/test/java/org/apache/commons/beanutils2/converters/ShortLocaleConverterTest.java
 
b/src/test/java/org/apache/commons/beanutils2/converters/ShortLocaleConverterTest.java
index 37ca9779..9f0eac61 100644
--- 
a/src/test/java/org/apache/commons/beanutils2/converters/ShortLocaleConverterTest.java
+++ 
b/src/test/java/org/apache/commons/beanutils2/converters/ShortLocaleConverterTest.java
@@ -204,12 +204,10 @@ class ShortLocaleConverterTest extends 
AbstractLocaleConverterTest<Short> {
         convertValueNoPattern(converter, "(B)", defaultIntegerValue, 
Short.valueOf("1"));
 
         // 
**************************************************************************
-        // Convert with non-localized pattern - unlike the equivalent 
BigDecimal Test Case
-        // it doesn't causes an exception in parse() - DecimalFormat parses it
-        // quite happily turning "1,234" into "1"
-        // Again this is one of the limitations of DecimalFormat
+        // Convert with non-localized pattern - the trailing characters left 
after the
+        // partial parse are now rejected, so the converter returns the 
default.
         // 
**************************************************************************
-        convertValueWithPattern(converter, "(B)", localizedIntegerValue, 
defaultIntegerPattern, Short.valueOf("1"));
+        convertValueWithPattern(converter, "(B)", localizedIntegerValue, 
defaultIntegerPattern, defaultValue);
 
         // 
**************************************************************************
         // Convert with specified type

Reply via email to