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 37b11859 reject non-integer strings in integer locale converters (#411)
37b11859 is described below

commit 37b118597f402e0c41a89ce9edc1420a8d4e1c78
Author: Dexter.k <[email protected]>
AuthorDate: Mon Jul 6 19:31:19 2026 +0000

    reject non-integer strings in integer locale converters (#411)
    
    The integer locale converters parsed a value like 5.5 with a DecimalFormat 
that allows decimals, then narrowed it, silently truncating to 5. Reject a 
non-integer parse result before narrowing; BigIntegerLocaleConverter uses 
toBigIntegerExact.
---
 .../locale/converters/BigIntegerLocaleConverter.java  |  6 +++++-
 .../locale/converters/ByteLocaleConverter.java        |  4 ++++
 .../locale/converters/IntegerLocaleConverter.java     |  4 ++++
 .../locale/converters/LongLocaleConverter.java        |  3 +++
 .../locale/converters/ShortLocaleConverter.java       |  4 ++++
 .../converters/BigIntegerLocaleConverterTest.java     | 19 ++++++++++++++-----
 .../converters/ByteLocaleConverterTest.java           | 19 ++++++++++++++-----
 .../converters/IntegerLocaleConverterTest.java        | 19 ++++++++++++++-----
 .../converters/LongLocaleConverterTest.java           | 19 ++++++++++++++-----
 .../converters/ShortLocaleConverterTest.java          | 19 ++++++++++++++-----
 10 files changed, 90 insertions(+), 26 deletions(-)

diff --git 
a/src/main/java/org/apache/commons/beanutils2/locale/converters/BigIntegerLocaleConverter.java
 
b/src/main/java/org/apache/commons/beanutils2/locale/converters/BigIntegerLocaleConverter.java
index 41dc4a47..73433fff 100644
--- 
a/src/main/java/org/apache/commons/beanutils2/locale/converters/BigIntegerLocaleConverter.java
+++ 
b/src/main/java/org/apache/commons/beanutils2/locale/converters/BigIntegerLocaleConverter.java
@@ -80,7 +80,11 @@ public class BigIntegerLocaleConverter extends 
DecimalLocaleConverter<BigInteger
             return (BigInteger) result;
         }
         if (result instanceof BigDecimal) {
-            return ((BigDecimal) result).toBigInteger();
+            try {
+                return ((BigDecimal) result).toBigIntegerExact();
+            } catch (final ArithmeticException e) {
+                throw new ConversionException("Supplied number is not an 
integer: " + result, e);
+            }
         }
         return BigInteger.valueOf(result.longValue());
     }
diff --git 
a/src/main/java/org/apache/commons/beanutils2/locale/converters/ByteLocaleConverter.java
 
b/src/main/java/org/apache/commons/beanutils2/locale/converters/ByteLocaleConverter.java
index 9119629f..a3286dda 100644
--- 
a/src/main/java/org/apache/commons/beanutils2/locale/converters/ByteLocaleConverter.java
+++ 
b/src/main/java/org/apache/commons/beanutils2/locale/converters/ByteLocaleConverter.java
@@ -74,6 +74,10 @@ public class ByteLocaleConverter extends 
DecimalLocaleConverter<Byte> {
         if (parsed.longValue() != parsed.byteValue()) {
             throw new ConversionException("Supplied number is not of type 
Byte: " + parsed.longValue());
         }
+        final double doubleValue = parsed.doubleValue();
+        if (doubleValue != Math.rint(doubleValue)) {
+            throw new ConversionException("Supplied number is not an integer: 
" + parsed);
+        }
         // now returns property Byte
         return Byte.valueOf(parsed.byteValue());
     }
diff --git 
a/src/main/java/org/apache/commons/beanutils2/locale/converters/IntegerLocaleConverter.java
 
b/src/main/java/org/apache/commons/beanutils2/locale/converters/IntegerLocaleConverter.java
index e805e492..0cfe6957 100644
--- 
a/src/main/java/org/apache/commons/beanutils2/locale/converters/IntegerLocaleConverter.java
+++ 
b/src/main/java/org/apache/commons/beanutils2/locale/converters/IntegerLocaleConverter.java
@@ -74,6 +74,10 @@ public class IntegerLocaleConverter extends 
DecimalLocaleConverter<Integer> {
         if (parsed.longValue() != parsed.intValue()) {
             throw new ConversionException("Supplied number is not of type 
Integer: " + parsed.longValue());
         }
+        final double doubleValue = parsed.doubleValue();
+        if (doubleValue != Math.rint(doubleValue)) {
+            throw new ConversionException("Supplied number is not an integer: 
" + parsed);
+        }
         return Integer.valueOf(parsed.intValue()); // unlike superclass it 
will return proper Integer
     }
 }
diff --git 
a/src/main/java/org/apache/commons/beanutils2/locale/converters/LongLocaleConverter.java
 
b/src/main/java/org/apache/commons/beanutils2/locale/converters/LongLocaleConverter.java
index 0fe51987..7f9ae377 100644
--- 
a/src/main/java/org/apache/commons/beanutils2/locale/converters/LongLocaleConverter.java
+++ 
b/src/main/java/org/apache/commons/beanutils2/locale/converters/LongLocaleConverter.java
@@ -79,6 +79,9 @@ public class LongLocaleConverter extends 
DecimalLocaleConverter<Long> {
         if (doubleValue < Long.MIN_VALUE || doubleValue > Long.MAX_VALUE) {
             throw new ConversionException("Supplied number is not of type 
Long: " + result);
         }
+        if (doubleValue != Math.rint(doubleValue)) {
+            throw new ConversionException("Supplied number is not an integer: 
" + result);
+        }
         return Long.valueOf(result.longValue());
     }
 }
diff --git 
a/src/main/java/org/apache/commons/beanutils2/locale/converters/ShortLocaleConverter.java
 
b/src/main/java/org/apache/commons/beanutils2/locale/converters/ShortLocaleConverter.java
index 570190cc..e2d2fad4 100644
--- 
a/src/main/java/org/apache/commons/beanutils2/locale/converters/ShortLocaleConverter.java
+++ 
b/src/main/java/org/apache/commons/beanutils2/locale/converters/ShortLocaleConverter.java
@@ -84,6 +84,10 @@ public class ShortLocaleConverter extends 
DecimalLocaleConverter<Short> {
         if (parsed.longValue() != parsed.shortValue()) {
             throw new ConversionException("Supplied number is not of type 
Short: " + parsed.longValue());
         }
+        final double doubleValue = parsed.doubleValue();
+        if (doubleValue != Math.rint(doubleValue)) {
+            throw new ConversionException("Supplied number is not an integer: 
" + parsed);
+        }
         // now returns property Short
         return Short.valueOf(parsed.shortValue());
     }
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 11d7f09e..476a6f8e 100644
--- 
a/src/test/java/org/apache/commons/beanutils2/converters/BigIntegerLocaleConverterTest.java
+++ 
b/src/test/java/org/apache/commons/beanutils2/converters/BigIntegerLocaleConverterTest.java
@@ -176,12 +176,12 @@ class BigIntegerLocaleConverterTest extends 
AbstractLocaleConverterTest<BigInteg
         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" into "1"
-        // I guess this is one of the limitations of DecimalFormat
+        // Convert value in the wrong format - the localized (German) 
converter reads
+        // ',' as the decimal separator, so "1,234" parses to the fractional 
value
+        // 1.234; the integer converter now rejects a non-integer result and 
returns
+        // the default.
         // 
**************************************************************************
-        convertValueNoPattern(converter, "(B)", defaultIntegerValue, new 
BigInteger("1"));
+        convertValueNoPattern(converter, "(B)", defaultIntegerValue, 
defaultValue);
         // 
**************************************************************************
         // Convert with non-localized pattern - the trailing characters left 
after the
         // partial parse are now rejected, so the converter returns the 
default.
@@ -203,4 +203,13 @@ class BigIntegerLocaleConverterTest extends 
AbstractLocaleConverterTest<BigInteg
         convertInvalid(converter, "(C)", defaultValue);
         convertNull(converter, "(C)", defaultValue);
     }
+
+    /**
+     * Tests that a non-integer value is rejected rather than silently 
truncated to an integer.
+     */
+    @Test
+    void testNonIntegerRejected() {
+        converter = 
BigIntegerLocaleConverter.builder().setDefault(defaultValue).setLocale(defaultLocale).get();
+        convertValueNoPattern(converter, "non-integer", "5.5", defaultValue);
+    }
 }
diff --git 
a/src/test/java/org/apache/commons/beanutils2/converters/ByteLocaleConverterTest.java
 
b/src/test/java/org/apache/commons/beanutils2/converters/ByteLocaleConverterTest.java
index 609805c8..383d24fe 100644
--- 
a/src/test/java/org/apache/commons/beanutils2/converters/ByteLocaleConverterTest.java
+++ 
b/src/test/java/org/apache/commons/beanutils2/converters/ByteLocaleConverterTest.java
@@ -169,12 +169,12 @@ class ByteLocaleConverterTest extends 
AbstractLocaleConverterTest<Byte> {
         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 ",123" into "0"
-        // I guess this is one of the limitations of DecimalFormat
+        // Convert value in the wrong format - the localized (German) 
converter reads
+        // ',' as the decimal separator, so ",123" parses to the fractional 
value
+        // 0.123; the integer converter now rejects a non-integer result and 
returns
+        // the default.
         // 
**************************************************************************
-        convertValueNoPattern(converter, "(B)", defaultIntegerValue, 
Byte.valueOf("0"));
+        convertValueNoPattern(converter, "(B)", defaultIntegerValue, 
defaultValue);
         // 
**************************************************************************
         // Convert with non-localized pattern
         // 
**************************************************************************
@@ -196,4 +196,13 @@ class ByteLocaleConverterTest extends 
AbstractLocaleConverterTest<Byte> {
         convertInvalid(converter, "(C)", defaultValue);
         convertNull(converter, "(C)", defaultValue);
     }
+
+    /**
+     * Tests that a non-integer value is rejected rather than silently 
truncated to an integer.
+     */
+    @Test
+    void testNonIntegerRejected() {
+        converter = 
ByteLocaleConverter.builder().setDefault(defaultValue).setLocale(defaultLocale).get();
+        convertValueNoPattern(converter, "non-integer", "5.5", defaultValue);
+    }
 }
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 213986ef..9fcefe7c 100644
--- 
a/src/test/java/org/apache/commons/beanutils2/converters/IntegerLocaleConverterTest.java
+++ 
b/src/test/java/org/apache/commons/beanutils2/converters/IntegerLocaleConverterTest.java
@@ -164,12 +164,12 @@ class IntegerLocaleConverterTest extends 
AbstractLocaleConverterTest<Integer> {
         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" into "1"
-        // I guess this is one of the limitations of DecimalFormat
+        // Convert value in the wrong format - the localized (German) 
converter reads
+        // ',' as the decimal separator, so "1,234" parses to the fractional 
value
+        // 1.234; the integer converter now rejects a non-integer result and 
returns
+        // the default.
         // 
**************************************************************************
-        convertValueNoPattern(converter, "(B)", defaultIntegerValue, 
Integer.valueOf("1"));
+        convertValueNoPattern(converter, "(B)", defaultIntegerValue, 
defaultValue);
         // 
**************************************************************************
         // Convert with non-localized pattern - the trailing characters left 
after the
         // partial parse are now rejected, so the converter returns the 
default.
@@ -215,4 +215,13 @@ class IntegerLocaleConverterTest extends 
AbstractLocaleConverterTest<Integer> {
         final int result = converter.convert(target, (Object) 
value.toString());
         assertEquals(value.intValue(), result, "Wrong result");
     }
+
+    /**
+     * Tests that a non-integer value is rejected rather than silently 
truncated to an integer.
+     */
+    @Test
+    void testNonIntegerRejected() {
+        converter = 
IntegerLocaleConverter.builder().setDefault(defaultValue).setLocale(defaultLocale).get();
+        convertValueNoPattern(converter, "non-integer", "5.5", defaultValue);
+    }
 }
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 dac385f3..ae15809b 100644
--- 
a/src/test/java/org/apache/commons/beanutils2/converters/LongLocaleConverterTest.java
+++ 
b/src/test/java/org/apache/commons/beanutils2/converters/LongLocaleConverterTest.java
@@ -168,12 +168,12 @@ class LongLocaleConverterTest extends 
AbstractLocaleConverterTest<Long> {
         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" into "1"
-        // I guess this is one of the limitations of DecimalFormat
+        // Convert value in the wrong format - the localized (German) 
converter reads
+        // ',' as the decimal separator, so "1,234" parses to the fractional 
value
+        // 1.234; the integer converter now rejects a non-integer result and 
returns
+        // the default.
         // 
**************************************************************************
-        convertValueNoPattern(converter, "(B)", defaultIntegerValue, 
Long.valueOf("1"));
+        convertValueNoPattern(converter, "(B)", defaultIntegerValue, 
defaultValue);
         // 
**************************************************************************
         // Convert with non-localized pattern - the trailing characters left 
after the
         // partial parse are now rejected, so the converter returns the 
default.
@@ -208,4 +208,13 @@ class LongLocaleConverterTest extends 
AbstractLocaleConverterTest<Long> {
         assertThrows(ConversionException.class, () -> 
converter.convert("99999999999999999999"));
         assertThrows(ConversionException.class, () -> 
converter.convert("-99999999999999999999"));
     }
+
+    /**
+     * Tests that a non-integer value is rejected rather than silently 
truncated to an integer.
+     */
+    @Test
+    void testNonIntegerRejected() {
+        converter = 
LongLocaleConverter.builder().setDefault(defaultValue).setLocale(defaultLocale).get();
+        convertValueNoPattern(converter, "non-integer", "5.5", defaultValue);
+    }
 }
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 7c752bf9..76b6834c 100644
--- 
a/src/test/java/org/apache/commons/beanutils2/converters/ShortLocaleConverterTest.java
+++ 
b/src/test/java/org/apache/commons/beanutils2/converters/ShortLocaleConverterTest.java
@@ -162,12 +162,12 @@ class ShortLocaleConverterTest extends 
AbstractLocaleConverterTest<Short> {
         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" into "1"
-        // I guess this is one of the limitations of DecimalFormat
+        // Convert value in the wrong format - the localized (German) 
converter reads
+        // ',' as the decimal separator, so "1,234" parses to the fractional 
value
+        // 1.234; the integer converter now rejects a non-integer result and 
returns
+        // the default.
         // 
**************************************************************************
-        convertValueNoPattern(converter, "(B)", defaultIntegerValue, 
Short.valueOf("1"));
+        convertValueNoPattern(converter, "(B)", defaultIntegerValue, 
defaultValue);
         // 
**************************************************************************
         // Convert with non-localized pattern - the trailing characters left 
after the
         // partial parse are now rejected, so the converter returns the 
default.
@@ -189,4 +189,13 @@ class ShortLocaleConverterTest extends 
AbstractLocaleConverterTest<Short> {
         convertInvalid(converter, "(C)", defaultValue);
         convertNull(converter, "(C)", defaultValue);
     }
+
+    /**
+     * Tests that a non-integer value is rejected rather than silently 
truncated to an integer.
+     */
+    @Test
+    void testNonIntegerRejected() {
+        converter = 
ShortLocaleConverter.builder().setDefault(defaultValue).setLocale(defaultLocale).get();
+        convertValueNoPattern(converter, "non-integer", "5.5", defaultValue);
+    }
 }

Reply via email to