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