You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2026/05/26 17:46:28 UTC

(commons-jexl) branch JEXL-459 created (now d6c1a046)

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

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


      at d6c1a046 JEXL-459: fix empty/size functions to handle exceptions properly;

This branch includes the following new commits:

     new d6c1a046 JEXL-459: fix empty/size functions to handle exceptions properly;

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(commons-jexl) 01/01: JEXL-459: fix empty/size functions to handle exceptions properly;

Posted by he...@apache.org.
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 <hb...@gmail.com>
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"));
+    }
+
 }