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

henrib pushed a commit to branch JEXL-459
in repository https://gitbox.apache.org/repos/asf/commons-jexl.git

commit d6c1a0463eb020771375f6eb5d0e1a10abeaf115
Author: Henrib <[email protected]>
AuthorDate: Tue May 26 19:46:17 2026 +0200

    JEXL-459: fix empty/size functions to handle exceptions properly;
---
 RELEASE-NOTES.txt                                  |  1 +
 src/changes/changes.xml                            |  1 +
 .../org/apache/commons/jexl3/JexlArithmetic.java   | 28 ++++++++++++++++++++++
 .../java/org/apache/commons/jexl3/JexlBuilder.java |  4 ++--
 .../apache/commons/jexl3/internal/Interpreter.java | 21 +++++++---------
 .../org/apache/commons/jexl3/Issues400Test.java    | 18 ++++++++++++++
 6 files changed, 59 insertions(+), 14 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index f760c168..0cef219e 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -14,6 +14,7 @@ This is a feature and maintenance release. Java 8 or later is 
required.
 Bugs Fixed in 3.6.3:
 ====================
 
+o JEXL-459:  Empty/size functions swallow all exceptions with no trace.
 o JEXL-458:  Improve permissions expressivity.
 o JEXL-457:  Reduce default exposure for RESTRICTED JexlPermissions.
 o JEXL-456:  Change in template parser behavior.
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 8243b110..93ee419f 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -30,6 +30,7 @@
         <release version="3.6.3" date="YYYY-MM-DD"
                  description="This is a feature and maintenance release. Java 
8 or later is required.">
             <!-- FIX -->
+            <action dev="henrib" type="fix" issue="JEXL-459" due-to="Mirek 
Hankus">Empty/size functions swallow all exceptions with no trace.</action>
             <action dev="henrib" type="fix" issue="JEXL-458" due-to="Daniil 
Averin">Improve permissions expressivity</action>
             <action dev="henrib" type="fix" issue="JEXL-457" due-to="Daniil 
Averin">Reduce default exposure for RESTRICTED JexlPermissions</action>
             <action dev="henrib" type="fix" issue="JEXL-456" due-to="Vincent 
Bussol">Change in template parser behavior.</action>
diff --git a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java 
b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
index 66f3b432..5399f2fa 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
@@ -30,11 +30,13 @@ import java.math.MathContext;
 import java.util.Collection;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
 import java.util.function.ToLongFunction;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.apache.commons.jexl3.introspection.JexlMethod;
+import org.apache.commons.logging.Log;
 
 /**
  * Perform arithmetic, implements JexlOperator methods.
@@ -1005,6 +1007,32 @@ public class JexlArithmetic {
         throw new ArithmeticException("Object "+(incr < 0? 
"decrement":"increment")+":(" + val + ")");
     }
 
+    /**
+     * Evaluates a supplier argument, eventually catching and logging any 
JexlException.
+     *
+     * <p>Used primarily by {@link #empty(Object)} and {@link #size(Object)} 
to guard argument evaluation.
+     * If evaluation succeeds, returns the supplier's result. If a {@link 
JexlException} occurs, logs a
+     * warning and returns the supplier itself.</p>
+     *
+     * <p>This method is public to allow overriding. Implementations that 
change the exception handling
+     * behavior must still return the supplier on failure to maintain the 
contract.</p>
+     *
+     * @param logger the logger for warning messages; may be null
+     * @param arg the supplier providing the argument to evaluate
+     * @return the evaluated result on success, or the supplier itself on 
JexlException
+     * @since 3.6.3
+     */
+    public Object evaluate(Log logger, Supplier<Object> arg) {
+        try {
+            return arg.get();
+        } catch (JexlException e) {
+            if (logger != null && logger.isWarnEnabled()) {
+                logger.warn(e.getMessage(), e);
+            }
+        }
+        return arg;
+    }
+
     /**
      * Check for emptiness of various types: Number, Collection, Array, Map, 
String.
      *
diff --git a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java 
b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
index 66b1d8df..dd410f6c 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
@@ -736,7 +736,7 @@ public class JexlBuilder {
      * Sets whether the engine will throw JexlException during evaluation when 
an error is triggered.
      * <p>When <em>not</em> silent, the engine throws an exception when the 
evaluation triggers an exception or an
      * error.</p>
-     * <p>It is recommended to use <em>silent(true)</em> as an explicit 
default.</p>
+     * <p>It is recommended to use <em>silent(false)</em> as an explicit 
default.</p>
      *
      * @param flag true means no JexlException will occur, false allows them
      * @return this builder
@@ -795,7 +795,7 @@ public class JexlBuilder {
     }
 
     /**
-     * Sets whether the engine considers unknown variables, methods, functions 
and constructors as errors or
+     * Sets whether the engine considers unknown variables, methods, 
functions, and constructors as errors or
      * evaluates them as null.
      * <p>When <em>not</em> strict, operators or functions using null operands 
return null on evaluation. When
      * strict, those raise exceptions.</p>
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java 
b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
index db0189f6..9b418ec0 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -23,6 +23,7 @@ import java.util.Objects;
 import java.util.Queue;
 import java.util.concurrent.Callable;
 import java.util.function.Consumer;
+import java.util.function.Supplier;
 
 import org.apache.commons.jexl3.JexlArithmetic;
 import org.apache.commons.jexl3.JexlContext;
@@ -1314,12 +1315,10 @@ public class Interpreter extends InterpreterBase {
 
     @Override
     protected Object visit(final ASTEmptyFunction node, final Object data) {
-        try {
-            final Object value = node.jjtGetChild(0).jjtAccept(this, data);
-            return operators.empty(node, value);
-        } catch (final JexlException xany) {
-            return true;
-        }
+        final JexlNode arg = node.jjtGetChild(0);
+        final Supplier<Object> eval = () -> arg.jjtAccept(this, data);
+        Object value = arithmetic.evaluate(logger, eval);
+        return value == eval ? true : operators.empty(node, value);
     }
 
     @Override
@@ -2008,12 +2007,10 @@ public class Interpreter extends InterpreterBase {
 
     @Override
     protected Object visit(final ASTSizeFunction node, final Object data) {
-        try {
-            final Object val = node.jjtGetChild(0).jjtAccept(this, data);
-            return operators.size(node, val);
-        } catch (final JexlException xany) {
-            return 0;
-        }
+        final JexlNode arg = node.jjtGetChild(0);
+        final Supplier<Object> eval = () -> arg.jjtAccept(this, data);
+        Object value = arithmetic.evaluate(logger, eval);
+        return value == eval ? 0 : operators.size(node, value);
     }
 
     @Override
diff --git a/src/test/java/org/apache/commons/jexl3/Issues400Test.java 
b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
index 6cab8ce3..f3e8ca66 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues400Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
@@ -33,6 +33,7 @@ import java.io.File;
 import java.io.StringWriter;
 import java.lang.reflect.Method;
 import java.math.BigDecimal;
+import java.math.MathContext;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -45,6 +46,7 @@ import java.util.function.Function;
 
 import org.apache.commons.jexl3.internal.Debugger;
 import org.apache.commons.jexl3.internal.Engine32;
+import org.apache.commons.jexl3.internal.Operator;
 import org.apache.commons.jexl3.internal.Scope;
 import org.apache.commons.jexl3.internal.TemplateEngine;
 import org.apache.commons.jexl3.introspection.JexlPermissions;
@@ -53,6 +55,7 @@ import org.apache.commons.jexl3.parser.ASTJexlScript;
 import org.apache.commons.jexl3.parser.JexlScriptParser;
 import org.apache.commons.jexl3.parser.Parser;
 import org.apache.commons.jexl3.parser.StringProvider;
+import org.apache.commons.logging.Log;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
@@ -1183,5 +1186,20 @@ public class Issues400Test {
         assertEquals("42", writer.toString());
     }
 
+
+    @Test
+    void test459() {
+        CaptureLog capture = new CaptureLog("test459");
+        final JexlBuilder builder = new 
JexlBuilder().logger(capture).safe(true).strict(false).silent(true);
+        final JexlEngine jexl = builder.create();
+        String expr = "empty('aaa'.substring(400,500))";
+        JexlScript script = jexl.createScript(expr);
+        Object result = script.execute(null);
+        assertEquals(true, result);
+        List<String> errors = capture.getCapturedMessages();
+        assertEquals(1, errors.size());
+        assertTrue(errors.get(0).contains("substring"));
+    }
+
 }
 

Reply via email to