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

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


The following commit(s) were added to refs/heads/master by this push:
     new 5d358a1a JEXL-451, JEXL-450: added specific tests; - fixed a few nits 
in changes, release notes; - deprecated unused methods;
5d358a1a is described below

commit 5d358a1afd26f238a03ea8bdcf6c7e754ab68a26
Author: Henrib <[email protected]>
AuthorDate: Wed Dec 3 17:45:29 2025 +0100

    JEXL-451, JEXL-450: added specific tests;
    - fixed a few nits in changes, release notes;
    - deprecated unused methods;
---
 RELEASE-NOTES.txt                                  |  56 +--------
 src/changes/changes.xml                            |  28 +++--
 .../org/apache/commons/jexl3/internal/Engine.java  |  10 +-
 .../apache/commons/jexl3/internal/Engine32.java    |   2 +
 src/site/xdoc/relnotes35.xml                       |   2 +-
 .../org/apache/commons/jexl3/Issues400Test.java    | 137 ++++++++++++++++++---
 6 files changed, 152 insertions(+), 83 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index df709786..ad417f70 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -1,61 +1,7 @@
 
-Apache Commons JEXL 3.6.0 Release Notes
----------------------------------------
-
-The Apache Commons JEXL team is pleased to announce the release of Apache 
Commons JEXL 3.6.0.
-
-Introduction
-------------
-
-Apache Commons JEXL is a library that facilitates the implementation of 
scripting features in
-        applications and frameworks written in Java.
-
-This is a feature and maintenance release. Java 8 or later is required.
-
-New features
-------------
-
-o JEXL-440:  Switch statement & expressions. 
-
-Fixed Bugs
-----------
-
-o JEXL-448:  Engines caching misses local variables handling and global 
eviction capability.
-o JEXL-447:  Regression in script-defined functions. Thanks to William Price. 
-o JEXL-446:  ClassTool module inspection is too strict. Thanks to William 
Price. 
-o JEXL-442:  Local variables are not resolved in interpolation string 
expression. Thanks to Xu Pengcheng. 
-o JEXL-441:  Tokenization error if "\n" in template expression. Thanks to Xu 
Pengcheng. 
-o JEXL-439:  When using reference capture, incorrect scoping when local 
variable redefines a captured symbol. Thanks to Xu Pengcheng. 
-o JEXL-437:  Semicolons not actually optional between function calls on 
separate lines. Thanks to William Price. 
-o          
org.apache.commons.jexl3.internal.introspection.AbstractExecutor.initMarker(Class,
 String, Class...) throws IllegalArgumentException instead of Exception. Thanks 
to Gary Gregory. 
-o          Reuse BigInteger constants instead of creating new instances in 
JexlArithmetic.toBigInteger(Object). Thanks to Gary Gregory. 
-
-Changes
--------
-
-o          Bump org.apache.commons:commons-parent from 81 to 91 #344, #368, 
#372. Thanks to Gary Gregory, Dependabot. 
-o          Bump org.apache.commons:commons-lang3 from 3.17.0 to 3.19.0 #355. 
Thanks to Gary Gregory, Dependabot. 
-o          Bump com.google.code.gson:gson from 2.13.1 to 2.13.2 #371. Thanks 
to Gary Gregory, Dependabot. 
-
-
-Historical list of changes: 
https://commons.apache.org/proper/commons-jexl/changes.html
-
-For complete information on Apache Commons JEXL, including instructions on how 
to submit bug reports,
-patches, or suggestions for improvement, see the Apache Commons JEXL website:
-
-https://commons.apache.org/proper/commons-jexl/
-
-Download page: https://commons.apache.org/proper/commons-jexl/download_jexl.cgi
-
-Have fun!
--Apache Commons Team
-
-------------------------------------------------------------------------------
-
-
 
                             Apache Commons JEXL
-                               Version 3.5.0
+                               Version 3.6.0
                                Release Notes
 
 
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index d4a82f91..1c8acf92 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -27,14 +27,26 @@
         <author email="[email protected]">Commons Developers</author>
     </properties>
     <body>
-        <release version="3.6.1" 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-449" due-to="William 
Price">Inconsistency on nature of curly-bracket syntactic elements regarding 
annotations.</action>
-           <action dev="henrib" type="fix" issue="JEXL-451" due-to="Xu 
Pengcheng, Henri Biestro">Restore JexlSandbox permission capabilities on 
Object.class.</action>
-           <!-- ADD -->
-           <!-- UPDATE -->
-          <action type="update" dev="ggregory" due-to="Gary Gregory, 
Dependabot">Bump org.apache.commons:commons-parent from 91 to 93 #391, 
#396.</action>
-          <action type="update" dev="ggregory" due-to="Gary Gregory">Bump 
org.apache.commons:commons-lang3 from 3.17.0 to 3.20.0.</action>
+        <release version="3.6.1" 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-449" due-to="William 
Price">Inconsistency on nature of
+                curly-bracket syntactic elements regarding annotations.
+            </action>
+            <action dev="henrib" type="fix" issue="JEXL-450">Disable 
instantiation of internal classes in RESTRICTED
+                mode.
+            </action>
+            <action dev="henrib" type="fix" issue="JEXL-451" due-to="Xu 
Pengcheng, Henri Biestro">Restore JexlSandbox
+                permission capabilities on Object.class.
+            </action>
+            <!-- ADD -->
+            <!-- UPDATE -->
+            <action type="update" dev="ggregory" due-to="Gary Gregory, 
Dependabot">Bump
+                org.apache.commons:commons-parent from 91 to 93 #391, #396.
+            </action>
+            <action type="update" dev="ggregory" due-to="Gary Gregory">Bump 
org.apache.commons:commons-lang3 from 3.17.0
+                to 3.20.0.
+            </action>
         </release>
         <release version="3.6.0" date="2025-11-10"
                  description="This is a feature and maintenance release. Java 
8 or later is required.">
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Engine.java 
b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
index c494aba0..5c8126a1 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Engine.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
@@ -337,6 +337,9 @@ public class Engine extends JexlEngine implements 
JexlUberspect.ConstantResolver
         final JexlUberspect uber = conf.uberspect() == null
                 ? getUberspect(conf.logger(), conf.strategy(), 
conf.permissions())
                 : conf.uberspect();
+        if (uber == null) {
+            throw new IllegalArgumentException("uberspect cannot be null");
+        }
         final ClassLoader loader = conf.loader();
         if (loader != null) {
             uber.setClassLoader(loader);
@@ -370,9 +373,6 @@ public class Engine extends JexlEngine implements 
JexlUberspect.ConstantResolver
         this.metaCache = new MetaCache(factory == null ? SoftCache::new : 
factory);
         this.cache = metaCache.createCache(conf.cache());
         this.cacheThreshold = conf.cacheThreshold();
-        if (uberspect == null) {
-            throw new IllegalArgumentException("uberspect cannot be null");
-        }
         this.parserFactory = conf.parserFactory() == null ?
                () -> new Parser(new StringProvider(";"))
                 : conf.parserFactory();
@@ -550,7 +550,9 @@ public class Engine extends JexlEngine implements 
JexlUberspect.ConstantResolver
      * @param script the script
      * @return the local variables array which may be empty (but not null) if 
no local variables were defined
      * @since 3.0
+     * @deprecated 3.6.1
      */
+    @Deprecated()
     protected String[] getLocalVariables(final JexlScript script) {
         return script.getLocalVariables();
     }
@@ -569,7 +571,9 @@ public class Engine extends JexlEngine implements 
JexlUberspect.ConstantResolver
      * @param script the script
      * @return the parameters which may be empty (but not null) if no 
parameters were defined
      * @since 3.0
+     * @deprecated 3.6.1
      */
+    @Deprecated
     protected String[] getParameters(final JexlScript script) {
         return script.getParameters();
     }
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Engine32.java 
b/src/main/java/org/apache/commons/jexl3/internal/Engine32.java
index eb403c44..b12b1b9d 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Engine32.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Engine32.java
@@ -31,7 +31,9 @@ import org.apache.commons.jexl3.parser.JexlNode;
 
 /**
  * An Engine that behaves like JEXL 3.2, bugs included.
+ * @deprecated 3.6.1, use Engine with JexlOptions instead.
  */
+@Deprecated
 public class Engine32 extends Engine {
     /**
      * Static delegation of getVariable.
diff --git a/src/site/xdoc/relnotes35.xml b/src/site/xdoc/relnotes35.xml
index 75c683a0..5f646f83 100644
--- a/src/site/xdoc/relnotes35.xml
+++ b/src/site/xdoc/relnotes35.xml
@@ -18,7 +18,7 @@
 
 <document>
   <properties>
-    <title>Apache Commons JEXL 3.5 Release Notes</title>
+    <title>Apache Commons JEXL 3.6 Release Notes</title>
   </properties>
 
   <body>
diff --git a/src/test/java/org/apache/commons/jexl3/Issues400Test.java 
b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
index 7a494a62..1ec15961 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues400Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
@@ -42,8 +42,11 @@ import java.util.Objects;
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.commons.jexl3.internal.Debugger;
+import org.apache.commons.jexl3.internal.Engine32;
 import org.apache.commons.jexl3.internal.Scope;
+import org.apache.commons.jexl3.internal.TemplateEngine;
 import org.apache.commons.jexl3.introspection.JexlPermissions;
+import org.apache.commons.jexl3.introspection.JexlSandbox;
 import org.apache.commons.jexl3.parser.ASTJexlScript;
 import org.apache.commons.jexl3.parser.JexlScriptParser;
 import org.apache.commons.jexl3.parser.Parser;
@@ -805,7 +808,8 @@ public class Issues400Test {
         final JexlScript script = jexl.createScript(src, "a", "b");
         final Object result = script.execute(null, "a", "b");
         Assertions.assertEquals("a\n?= ba\n?== b", result);
-
+        String[] locals =  script.getLocalVariables();
+        Assertions.assertArrayEquals(new String[]{"c", "foo"}, locals);
         final String TEST447 = "src/test/scripts/test447.jexl";
         final File src447 = new File(TEST447);
         final JexlScript script447 = jexl.createScript(src447);
@@ -831,31 +835,132 @@ public class Issues400Test {
 
     }
 
+    @Test
+    void test450a() {
+        JexlEngine jexl0 = new 
JexlBuilder().silent(false).permissions(JexlPermissions.RESTRICTED).create();
+        assertThrows(JexlException.Method.class, ()->jexl0.newInstance(
+            "org.apache.commons.jexl3.internal.introspection.Uberspect", null, 
null),
+            "should not be able to create Uberspect with RESTRICTED");
+        JexlPermissions perm = new 
JexlPermissions.ClassPermissions(org.apache.commons.jexl3.internal.introspection.Uberspect.class);
+        JexlEngine jexl1 = new 
JexlBuilder().silent(false).permissions(perm).create();
+        assertNotNull(jexl1.newInstance(
+                        
"org.apache.commons.jexl3.internal.introspection.Uberspect", null, null),
+                "should able to create Uberspect with Uberspect permission");
+
+    }
+
+    @Test
+    void test450b() {
+        // cannot load System with RESTRICTED
+        assertThrows(JexlException.Method.class,
+                () -> run450b(JexlPermissions.RESTRICTED), "should not be able 
to load System with RESTRICTED");
+        // can load System with UNRESTRICTED
+        assertEquals(java.lang.System.class, run450b(UNRESTRICTED));
+        // need to explicitly allow Uberspect and the current class loader to 
load System
+        JexlPermissions perm = new JexlPermissions.ClassPermissions(
+               getClass().getClassLoader().getClass(), 
org.apache.commons.jexl3.internal.introspection.Uberspect.class);
+        assertEquals(java.lang.System.class, run450b(perm));
+    }
+
+    private static Object run450b(JexlPermissions perm) {
+        JexlEngine jexl = new 
JexlBuilder().silent(false).permissions(perm).create();
+        String uscript = 
"new('org.apache.commons.jexl3.internal.introspection.Uberspect', null, null, 
perm).getClassLoader().loadClass('java.lang.System')";
+        JexlScript u0 = jexl.createScript(uscript, "perm");
+        return u0.execute(null, perm);
+    }
+
+    @Test
+    void test450c() {
+        // can reach and invoke System::currentTimeMillis with UNRESTRICTED
+        assertNotNull(run450c(UNRESTRICTED));
+        // need explicit permissions to ClassPermissions and Uberspect to 
reach and invoke System::currentTimeMillis
+        JexlPermissions perm = new JexlPermissions.ClassPermissions(
+            JexlPermissions.ClassPermissions.class,
+            org.apache.commons.jexl3.internal.introspection.Uberspect.class);
+        assertNotNull(run450c(perm));
+        // cannot reach and invoke System::currentTimeMillis with RESTRICTED
+        assertThrows(JxltEngine.Exception.class,
+                () -> run450c(JexlPermissions.RESTRICTED), "should not be able 
to load System with RESTRICTED");
+    }
+
+    private static Object run450c(JexlPermissions perm) {
+        JexlBuilder builder = new 
JexlBuilder().silent(false).permissions(perm);
+        Object result = new TemplateEngine(new Engine32(builder),false, 2, 
'$', '#').createExpression(
+            "${x = new 
('org.apache.commons.jexl3.internal.introspection.Uberspect', null, null, 
UNRESTRICTED);" +
+            "sys = x?.getClassLoader()?.loadClass('java.lang.System') ?: 
SYSTEM;" + // fail to create uberspect with java 8
+            "p = 
new('org.apache.commons.jexl3.introspection.JexlPermissions$ClassPermissions', 
[sys]);" +
+            "c = 
new('org.apache.commons.jexl3.internal.introspection.Uberspect', null, null, 
p);" +
+            "z = c.getMethod(sys,'currentTimeMillis').invoke(x,null);}"
+        ).evaluate(new BrkContext());
+        return result;
+    }
+
     @Test
     void test450() {
-        for (JexlPermissions perm : new 
JexlPermissions[]{JexlPermissions.RESTRICTED, JexlPermissions.UNRESTRICTED}) {
-            JexlEngine jexl = new JexlBuilder().permissions(perm).create();
-            JexlScript e = getExpression450(jexl);
-            try {
-                e.execute(null);
-                fail("should not be able to access System class with " + perm);
-            } catch (JexlException xjexl) {
-                assertNotNull(xjexl);
-            }
+        assertNotNull(run450(JexlPermissions.UNRESTRICTED),
+                "should be able to reach and invoke System::currentTimeMillis 
with UNRESTRICTED");
+        assertNotNull(run450(new 
JexlPermissions.ClassPermissions(org.apache.commons.jexl3.internal.TemplateEngine.class)),
+                "should be able to reach and invoke System::currentTimeMillis 
with TemplateEngine permission");
+        assertThrows(JexlException.Method.class,
+                () -> run450(RESTRICTED),
+                "should not be able to reach and invoke 
System::currentTimeMillis with RESTRICTED");
+    }
+
+    public static class Engine33 extends Engine32 {
+        public Engine33() {
+            this(createBuilder());
+        }
+        public Engine33(JexlBuilder builder) {
+            super(builder);
+        }
+        static JexlBuilder createBuilder() {
+            JexlPermissions perm = new JexlPermissions.ClassPermissions(
+                    Issues400Test.class.getClassLoader().getClass(),
+                    JexlPermissions.ClassPermissions.class,
+                    org.apache.commons.jexl3.internal.TemplateEngine.class,
+                    
org.apache.commons.jexl3.internal.introspection.Uberspect.class);
+            return new 
JexlBuilder().safe(false).silent(false).permissions(perm);
         }
     }
 
-    private static JexlScript getExpression450(JexlEngine jexl) {
+    private static Object run450(JexlPermissions perm) {
+        JexlEngine jexl = new 
JexlBuilder().silent(false).strict(true).safe(false).permissions(perm).create();
         return 
jexl.createScript("new('org.apache.commons.jexl3.internal.TemplateEngine'," +
-            
"new('org.apache.commons.jexl3.internal.Engine32'),false,256,'{'.charAt(0),'#'.charAt(0))"
 +
+            
"new('org.apache.commons.jexl3.Issues400Test$Engine33'),false,256,'$'.charAt(0),'#'.charAt(0))"
 +
                 ".createExpression(" +
-                    "\"#{x = new 
('org.apache.commons.jexl3.internal.introspection.Uberspect', null, null, 
UNRESTRICTED);" +
-                    "sys = x.getClassLoader().loadClass('java.lang.System') ?: 
SYSTEM;" + // fail to load System
+                    "\"#{x = new 
('org.apache.commons.jexl3.internal.introspection.Uberspect', null, null);" +
+                    "sys = x?.getClassLoader().loadClass('java.lang.System') 
?: SYSTEM;" + // fail to load System on Java 8
                     "p = 
new('org.apache.commons.jexl3.introspection.JexlPermissions$ClassPermissions', 
[sys]);" +
                     "c = 
new('org.apache.commons.jexl3.internal.introspection.Uberspect', null, null, 
p);" +
                     "z = 
c.getMethod(sys,'currentTimeMillis').invoke(x,null);}\")" +
-                    
".evaluate(new('org.apache.commons.jexl3.Issues400Test$BrkContext'))");
+                    
".evaluate(new('org.apache.commons.jexl3.Issues400Test$BrkContext'))").execute(null);
+    }
+
+    @Test
+    void test451() {
+        JexlEngine jexl = new JexlBuilder().create();
+        assertEquals("42",
+                jexl.createScript("o.toString()", "o").execute(null, "42"));
+        JexlPermissions perms = RESTRICTED.compose("java.lang { +Class { 
getSimpleName(); } }");
+        JexlSandbox sandbox = new JexlSandbox(false, true);
+        sandbox.permissions(Object.class.getName(), true, true, false, false);
+        sandbox.allow(String.class.getName()).execute("toString");
+        final JexlEngine jexl451 = new 
JexlBuilder().safe(false).silent(false).permissions(perms).sandbox(sandbox).create();
+        // sandbox allows String::toString
+        assertEquals("42",
+                jexl451.createScript("o.toString()", "o").execute(null, "42"));
+        // sandbox forbids getClass
+        assertThrows(JexlException.Method.class,
+                () -> jexl451.createScript("oo.getClass()", 
"oo").execute(null, "42"));
+        // sandbox allows reading properties, permissions allow getClass
+        assertEquals(String.class,
+                jexl451.createScript("o.class", "o").execute(null, "42"));
+        // sandbox allows reading properties, permissions allow getSimpleName
+        assertEquals("Object",
+                jexl451.createScript("o.class.simpleName", "o").execute(null, 
new Object()));
+        // sandbox allows reading properties, permissions forbids 
getClassLoader
+        assertThrows(JexlException.Property.class,
+                () -> jexl451.createScript("o.class.classLoader", 
"o").execute(null, new Object()));
     }
-
 }
 

Reply via email to