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

markt-asf pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/10.1.x by this push:
     new 450e1ba0c9 Review "Parse exponent-only numbers in the JSON parser"
450e1ba0c9 is described below

commit 450e1ba0c9859a66b0839d4f733bc50f3c5a83b6
Author: Mark Thomas <[email protected]>
AuthorDate: Tue Nov 3 14:51:15 2026 +0000

    Review "Parse exponent-only numbers in the JSON parser"
    
    Describe behaviour change in changelog
    Treat out of range number as a ParseException
---
 java/org/apache/tomcat/util/json/JSONParser.java   | 12 ++++--
 java/org/apache/tomcat/util/json/JSONParser.jjt    |  8 +++-
 .../apache/tomcat/util/json/TestJSONParser.java    | 48 ++++++++++++++++++++++
 webapps/docs/changelog.xml                         | 17 ++++++++
 4 files changed, 81 insertions(+), 4 deletions(-)

diff --git a/java/org/apache/tomcat/util/json/JSONParser.java 
b/java/org/apache/tomcat/util/json/JSONParser.java
index 534692eb20..7abfab60a4 100644
--- a/java/org/apache/tomcat/util/json/JSONParser.java
+++ b/java/org/apache/tomcat/util/json/JSONParser.java
@@ -448,10 +448,16 @@ public class JSONParser implements JSONParserConstants {
                         }
                     }
                 } else {
-                    {
-                        if ("" != null) {
-                            return new java.math.BigDecimal(t.image);
+                    try {
+                        {
+                            if ("" != null) {
+                                return new java.math.BigDecimal(t.image);
+                            }
                         }
+                    } catch (NumberFormatException nfe) {
+                        // The grammar admits exponents that BigDecimal can't 
represent. Report them as a parse
+                        // error rather than letting an unchecked exception 
escape the parser.
+                        throw new ParseException("Exponent out of range in 
number [" + t.image + "]");
                     }
                 }
                 break;
diff --git a/java/org/apache/tomcat/util/json/JSONParser.jjt 
b/java/org/apache/tomcat/util/json/JSONParser.jjt
index c50f41935a..89c05f3ae8 100644
--- a/java/org/apache/tomcat/util/json/JSONParser.jjt
+++ b/java/org/apache/tomcat/util/json/JSONParser.jjt
@@ -413,7 +413,13 @@ Number number(): {
             if (nativeNumbers) {
                 return Double.valueOf(t.image);
             } else {
-                return new java.math.BigDecimal(t.image);
+                try {
+                    return new java.math.BigDecimal(t.image);
+                } catch (NumberFormatException nfe) {
+                    // The grammar admits exponents that BigDecimal can't 
represent. Report them as a parse error
+                    // rather than letting an unchecked exception escape the 
parser.
+                    throw new ParseException("Exponent out of range in number 
[" + t.image + "]");
+                }
             }
         }
     ) | (
diff --git a/test/org/apache/tomcat/util/json/TestJSONParser.java 
b/test/org/apache/tomcat/util/json/TestJSONParser.java
index 9771bbf0bb..47f0588cb8 100644
--- a/test/org/apache/tomcat/util/json/TestJSONParser.java
+++ b/test/org/apache/tomcat/util/json/TestJSONParser.java
@@ -111,6 +111,54 @@ public class TestJSONParser {
         Assert.assertEquals(BigInteger.ONE, result.get("1E+5"));
     }
 
+    /*
+     * The exponent of a number must be one that BigDecimal can represent. A 
larger exponent must be reported as a
+     * ParseException rather than as an unchecked NumberFormatException.
+     */
+    @Test
+    public void testExponentOutOfRangeIsParseException() {
+        for (String number : new String[] { "1e-2147483649", "1e2147483649", 
"1.5e-2147483649", "-1E2147483649" }) {
+            try {
+                new JSONParser("[" + number + "]").parse();
+                Assert.fail("A ParseException was expected for " + number);
+            } catch (ParseException e) {
+                Assert.assertTrue(e.getMessage(), 
e.getMessage().contains(number));
+            }
+        }
+    }
+
+    @Test
+    public void testExponentOutOfRangeAsKeyIsParseException() {
+        Assert.assertThrows(ParseException.class, () -> new 
JSONParser("{1e2147483649:1}").parseObject());
+    }
+
+    @Test
+    public void testLargestExponentsAccepted() throws ParseException {
+        JSONParser parser = new JSONParser("[1e2147483647, 1e-2147483647]");
+        List<Object> values = parser.parseArray();
+        Assert.assertEquals(new BigDecimal("1e2147483647"), values.get(0));
+        Assert.assertEquals(new BigDecimal("1e-2147483647"), values.get(1));
+    }
+
+    @Test
+    public void testExponentWithLeadingZeros() throws ParseException {
+        Assert.assertEquals(new BigDecimal("1E5"), new 
JSONParser("1e05").parse());
+    }
+
+    /*
+     * Bare words that start with a number but are not numbers are not 
affected by the exponent-only number syntax.
+     */
+    @Test
+    public void testBareWordKeysStartingWithNumber() throws ParseException {
+        Map<String, Object> result = new JSONParser("{1e5x:1, 12abc:2, 1e:3, 
3E2:4}").parseObject();
+        Assert.assertEquals(BigInteger.valueOf(1), result.get("1e5x"));
+        Assert.assertEquals(BigInteger.valueOf(2), result.get("12abc"));
+        Assert.assertEquals(BigInteger.valueOf(3), result.get("1e"));
+        // An exponent-only number is a number, so its key is the normalised 
form of the number
+        Assert.assertEquals(BigInteger.valueOf(4), result.get("3E+2"));
+        Assert.assertEquals(4, result.size());
+    }
+
     private static String nest(int depth) {
         StringBuilder result = new StringBuilder(depth * 2);
         for (int i = 0; i < depth; i++) {
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index dcb8e30406..44aecbedae 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -383,6 +383,23 @@
         corrected to terminate, since the Java shift distance is evaluated
         modulo 32. (remm)
       </fix>
+      <fix>
+        The JSON parser now accepts numbers that have an exponent but no
+        fraction, such as <code>1e5</code>, as required by RFC 8259. Note that
+        when the lenient dialect is used, a bare object key that is such a
+        number (for example <code>{1e5:1}</code>) is now a number and the key
+        is the string form of the parsed number (<code>1E+5</code>) rather than
+        the original text, as already happened for bare keys that have both a
+        fraction and an exponent. Quoted keys are not affected. Numbers with an
+        exponent that is too large or too small to be represented are now
+        rejected with a <code>ParseException</code> rather than an unchecked
+        <code>NumberFormatException</code>. (remm)
+      </fix>
+      <fix>
+        With native numbers enabled, the JSON parser now returns integers that
+        do not fit in a <code>long</code> as <code>BigInteger</code> rather 
than
+        failing with an unchecked <code>NumberFormatException</code>. (remm)
+      </fix>
       <fix>
         The JSON parser now rejects input with a nesting depth greater than
         1000 with a <code>ParseException</code>. Previously, deeply nested


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to