SLING-10342: The recursion selector value for the JsonRenderServlet s… (#7)

* SLING-10342: The recursion selector value for the JsonRenderServlet should be limited to real numbers
diff --git a/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonRenderer.java b/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonRenderer.java
index 975f520..b1648c4 100644
--- a/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonRenderer.java
+++ b/src/main/java/org/apache/sling/servlets/get/impl/helpers/JsonRenderer.java
@@ -18,12 +18,12 @@
 
 import java.io.IOException;
 import java.io.StringWriter;
+import java.math.BigInteger;
 
 import javax.json.Json;
 import javax.json.stream.JsonGenerator;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.commons.lang3.StringUtils;
 import org.apache.sling.api.SlingException;
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.SlingHttpServletResponse;
@@ -160,14 +160,11 @@
                     maxRecursionLevels = -1;
                 } else {
                     try {
-                        maxRecursionLevels = Integer.parseInt(level);
+                        maxRecursionLevels = parseRecursionLevel(level);
+                    } catch (ArithmeticException ae) {
+                        maxRecursionLevels = -1;
                     } catch (NumberFormatException nfe) {
-                        //SLING-2324
-                        if (StringUtils.isNumeric(level)){
-                            maxRecursionLevels = -1;
-                        } else {
-                            throw new IllegalArgumentException("Invalid recursion selector value '" + level + "'");
-                        }
+                        throw new IllegalArgumentException("Invalid recursion selector value '" + level + "'");
                     }
                 }
             }
@@ -176,6 +173,27 @@
     }
 
     /**
+     * parse the int value from an input string but only when the input is a real number and >= -1 i.e., [0-9]+ | -1
+     * @param input
+     * @return the value of the number as an int
+     * @throws ArithmeticException - if the input was a real positive number but didn't fit into an int
+     * @throws IllegalArgumentException - if the input was not a real number or out of bounds
+     */
+    private int parseRecursionLevel(String input) throws ArithmeticException, IllegalArgumentException {
+        if ("-1".equals(input)) {
+            return -1;
+        }
+        BigInteger inputNumber = new BigInteger(input);
+        if (!inputNumber.toString().equals(input)) {
+            throw new NumberFormatException("Not a real number string");
+        }
+        if (inputNumber.signum() == -1) {
+            throw new NumberFormatException("Not a valid negative number");
+        }
+        return inputNumber.intValueExact();
+    }
+
+    /**
      * Checks if the provided request contains a certain selector.
      * @param req the request
      * @param selectorToCheck the selector
diff --git a/src/test/java/org/apache/sling/servlets/get/impl/helpers/JsonRendererTest.java b/src/test/java/org/apache/sling/servlets/get/impl/helpers/JsonRendererTest.java
index 720b483..b5ac6a0 100644
--- a/src/test/java/org/apache/sling/servlets/get/impl/helpers/JsonRendererTest.java
+++ b/src/test/java/org/apache/sling/servlets/get/impl/helpers/JsonRendererTest.java
@@ -99,6 +99,42 @@
         assertTrue(jrs.isTidy(request));
     }
 
+    @Test(expected = IllegalArgumentException.class)
+    public void testRecursionLevelNumeric() {
+        context.requestPathInfo().setSelectorString("᭙");
+        jrs.getMaxRecursionLevel(request);
+    }
+
+    @Test
+    public void testRecursionLevelOverflow() {
+        context.requestPathInfo().setSelectorString(Long.toString(((long) Integer.MAX_VALUE)  + 1L));
+        assertEquals(-1, jrs.getMaxRecursionLevel(request));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testRecursionLevelUnderflow() {
+        context.requestPathInfo().setSelectorString(Long.toString(((long) Integer.MIN_VALUE)  - 1L));
+        jrs.getMaxRecursionLevel(request);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testRecursionLevelNegativ() {
+        context.requestPathInfo().setSelectorString(Long.toString( - 2L));
+        jrs.getMaxRecursionLevel(request);
+    }
+
+    @Test
+    public void testRecursionLevelInfinity() {
+        context.requestPathInfo().setSelectorString("infinity");
+        assertEquals(-1, jrs.getMaxRecursionLevel(request));
+    }
+
+    @Test
+    public void testRecursionLevelInfinityNumeric() {
+        context.requestPathInfo().setSelectorString("-1");
+        assertEquals(-1, jrs.getMaxRecursionLevel(request));
+    }
+
     @Test
     public void testBadRequest() throws IOException {
         context.requestPathInfo().setSelectorString("bad.selectors");