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 2023/03/09 23:01:02 UTC
[commons-jexl] branch master updated: JEXL: getting ready for 3.3; - doc, expose 3.3 release notes to be clear about what can break on upgrade
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 84f4e885 JEXL: getting ready for 3.3; - doc, expose 3.3 release notes to be clear about what can break on upgrade
84f4e885 is described below
commit 84f4e88531b5d3e242fbefbefc5164669d168ada
Author: henrib <he...@apache.org>
AuthorDate: Fri Mar 10 00:00:54 2023 +0100
JEXL: getting ready for 3.3;
- doc, expose 3.3 release notes to be clear about what can break on upgrade
---
RELEASE-NOTES.txt | 11 +++
.../jexl3/introspection/JexlPermissions.java | 75 ++++++++++++++-
.../commons/jexl3/scripting/JexlScriptEngine.java | 30 ++++--
src/site/site.xml | 3 +-
src/site/xdoc/relnotes33.xml | 106 +++++++++++++++++++++
.../org/apache/commons/jexl3/ClassPermissions.java | 77 ---------------
.../apache/commons/jexl3/examples/StreamTest.java | 3 +-
.../jexl3/scripting/JexlScriptEngineTest.java | 8 +-
8 files changed, 220 insertions(+), 93 deletions(-)
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 1021cb07..043edcdb 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -27,6 +27,17 @@ Version 3.3 is a minor release.
Compatibility with previous releases
====================================
Version 3.3 is source and binary compatible with 3.2.
+However, the default setting for permissions that determine which packages, classes and methods are accessible
+to scripts has been reduced to a very narrow set. When migrating from previous version of JEXL, this may result
+in breaking your application behavior ; this breaking change requires remediation in your code.
+Despite the obvious inconvenience - our sincere apologies on the matter -, how much functional and semantic power is
+accessible through scripts has a real impact on your application security and stability ; that potential risk requires
+an informed review and conscious choice on your end.
+To mitigate the change, you can revert to the previous behavior with one line of code (see JexlBuilder) or use this
+opportunity to reduce exposure. Whether Files, URLs, networking, processes, class-loaders or reflection features are
+accessible are part of the choice to make.
+Any CVE linked to JEXL should try resolution by upgrading to 3.3.
+
What's new in 3.3:
==================
diff --git a/src/main/java/org/apache/commons/jexl3/introspection/JexlPermissions.java b/src/main/java/org/apache/commons/jexl3/introspection/JexlPermissions.java
index c4b7d276..d405b6cd 100644
--- a/src/main/java/org/apache/commons/jexl3/introspection/JexlPermissions.java
+++ b/src/main/java/org/apache/commons/jexl3/introspection/JexlPermissions.java
@@ -22,6 +22,12 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* This interface describes permissions used by JEXL introspection that constrain which
@@ -39,10 +45,22 @@ import java.lang.reflect.Modifier;
* processed.</p>
* <p>A simple textual configuration can be used to create user defined permissions using
* {@link JexlPermissions#parse(String...)}.</p>
+ *
*<p>To instantiate a JEXL engine using permissions, one should use a {@link org.apache.commons.jexl3.JexlBuilder}
* and call {@link org.apache.commons.jexl3.JexlBuilder#permissions(JexlPermissions)}. Another approach would
* be to instantiate a {@link JexlUberspect} with those permissions and call
* {@link org.apache.commons.jexl3.JexlBuilder#uberspect(JexlUberspect)}.</p>
+ *
+ * <p>
+ * To help migration from earlier versions, it is possible to revert to the JEXL 3.2 default lenient behavior
+ * by calling {@link org.apache.commons.jexl3.JexlBuilder#setDefaultPermissions(JexlPermissions)} with
+ * {@link #UNRESTRICTED} as parameter.
+ * </p>
+ * <p>
+ * For the same reason, using JEXL through scripting, it is possible to revert the underlying JEXL behaviour to
+ * JEXL 3.2 default by calling {@link org.apache.commons.jexl3.scripting.JexlScriptEngine#setPermissions(JexlPermissions)}
+ * with {@link #UNRESTRICTED} as parameter.
+ * </p>
* @since 3.3
*/
public interface JexlPermissions {
@@ -315,7 +333,7 @@ public interface JexlPermissions {
}
/**
- * A base for permission delegation allowing greater functional malleability.
+ * A base for permission delegation allowing functional refinement.
* Overloads should call the appropriate validate() method early in their body.
*/
class Delegate implements JexlPermissions {
@@ -356,4 +374,59 @@ public interface JexlPermissions {
return new Delegate(base.compose(src));
}
}
+
+ /**
+ * A permission delegation that augments the RESTRICTED permission with an explicit
+ * set of classes.
+ * <p>Typical use case is to deny access to a package - and thus all its classes - but allow
+ * a few specific classes.</p>
+ */
+ class ClassPermissions extends JexlPermissions.Delegate {
+ /** The set of explicitly allowed classes, overriding the delegate permissions. */
+ private final Set<String> allowedClasses;
+
+ /**
+ * Creates permissions based on the RESTRICTED set but allowing an explicit set.
+ * @param allow the set of allowed classes
+ */
+ public ClassPermissions(Class... allow) {
+ this(JexlPermissions.RESTRICTED,
+ Arrays.asList(Objects.requireNonNull(allow))
+ .stream().map(Class::getCanonicalName).collect(Collectors.toList()));
+ }
+
+ /**
+ * Required for compose().
+ * @param delegate the base to delegate to
+ * @param allow the list of class canonical names
+ */
+ public ClassPermissions(JexlPermissions delegate, Collection<String> allow) {
+ super(Objects.requireNonNull(delegate));
+ allowedClasses = new HashSet<>(Objects.requireNonNull(allow));
+ }
+
+ private boolean isClassAllowed(Class<?> clazz) {
+ return allowedClasses.contains(clazz.getCanonicalName());
+ }
+
+ @Override
+ public boolean allow(Class<?> clazz) {
+ return (validate(clazz) && isClassAllowed(clazz)) || super.allow(clazz);
+ }
+
+ @Override
+ public boolean allow(Method method) {
+ return (validate(method) && isClassAllowed(method.getDeclaringClass())) || super.allow(method);
+ }
+
+ @Override
+ public boolean allow(Constructor constructor) {
+ return (validate(constructor) && isClassAllowed(constructor.getDeclaringClass())) || super.allow(constructor);
+ }
+
+ @Override
+ public JexlPermissions compose(String... src) {
+ return new ClassPermissions(base.compose(src), allowedClasses);
+ }
+ }
}
diff --git a/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngine.java b/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngine.java
index 56642fd4..c9dfeb27 100644
--- a/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngine.java
+++ b/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngine.java
@@ -72,11 +72,30 @@ public class JexlScriptEngine extends AbstractScriptEngine implements Compilable
*/
private static Reference<JexlEngine> ENGINE = null;
+ /**
+ * The permissions used to create the script engine.
+ */
+ private static JexlPermissions PERMISSIONS = null;
+ /**
+ * Sets the permissions instance used to create the script engine.
+ * <p>Calling this method will force engine instance re-creation.</p>
+ * <p>To restore 3.2 script behavior:</p>
+ * <code>
+ * JexlScriptEngine.setPermissions(JexlPermissions.UNRESTRICTED);
+ * </code>
+ * @param permissions the permissions instance to use or null to use the {@link JexlBuilder} default
+ * @since 3.3
+ */
+ public static void setPermissions(final JexlPermissions permissions) {
+ PERMISSIONS = permissions;
+ ENGINE = null; // will force recreation
+ }
+
/**
* Sets the shared instance used for the script engine.
* <p>This should be called early enough to have an effect, ie before any
* {@link javax.script.ScriptEngineManager} features.</p>
- * <p>To restore 3.2 script permeability:</p>
+ * <p>To restore 3.2 script behavior:</p>
* <code>
* JexlScriptEngine.setInstance(new JexlBuilder()
* .cache(512)
@@ -84,12 +103,6 @@ public class JexlScriptEngine extends AbstractScriptEngine implements Compilable
* .permissions(JexlPermissions.UNRESTRICTED)
* .create());
* </code>
- * <p>Alternatively, setting the default {@link JexlBuilder#setDefaultPermissions(JexlPermissions)} using
- * {@link org.apache.commons.jexl3.introspection.JexlPermissions#UNRESTRICTED} will also restore JEXL 3.2
- * behavior.</p>
- * <code>
- * JexlBuilder.setDefaultPermissions(JexlPermissions.UNRESTRICTED);
- * </code>
* @param engine the JexlEngine instance to use
* @since 3.3
*/
@@ -111,6 +124,9 @@ public class JexlScriptEngine extends AbstractScriptEngine implements Compilable
.safe(false)
.logger(JexlScriptEngine.LOG)
.cache(JexlScriptEngine.CACHE_SIZE);
+ if (PERMISSIONS != null ) {
+ builder.permissions(PERMISSIONS);
+ }
engine = builder.create();
ENGINE = new SoftReference<>(engine);
}
diff --git a/src/site/site.xml b/src/site/site.xml
index 17b337dc..eec9a22f 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -25,7 +25,8 @@
<body>
<menu name="JEXL">
<item name="Overview" href="index.html" />
- <item name="Javadoc 3.2.1" href="apidocs/index.html"/>
+ <item name="JEXL 3.3 Release notes" href="relnotes33.html"/>
+ <item name="Javadoc 3.3" href="apidocs/index.html"/>
<item name="Javadoc 2.1.1" href="javadocs/apidocs-2.1.1/index.html"/>
<item name="Javadoc 1.1" href="javadocs/apidocs-1.1/index.html"/>
<item name="Download" href="download_jexl.cgi"/>
diff --git a/src/site/xdoc/relnotes33.xml b/src/site/xdoc/relnotes33.xml
new file mode 100644
index 00000000..982cfa89
--- /dev/null
+++ b/src/site/xdoc/relnotes33.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<document>
+ <properties>
+ <title>Apache Commons JEXL 3.3 Release Notes</title>
+ </properties>
+
+ <body>
+ <section name="Compatibility with previous releases">
+ <p>
+ Version 3.3 is source and binary compatible with 3.2.
+ </p><p>
+ However, the default setting for permissions that determine which packages, classes and methods are accessible
+ to scripts has been reduced to a very narrow set. When migrating from previous version of JEXL, this may result
+ in breaking your application behavior ; this breaking change requires remediation in your code.
+ </p><p>
+ Despite the obvious inconvenience - our sincere apologies on the matter -, how much functional and semantic power is
+ accessible through scripts has a real impact on your application security and stability ; that potential risk requires
+ an informed review and conscious choice on your end.
+ </p><p>
+ To mitigate the change, you can revert to the previous behavior with one line of code
+ (see <a href="apidocs/org/apache/commons/jexl3/introspection/JexlPermissions.html">JexlPermissions</a>,
+ <a href="apidocs/org/apache/commons/jexl3/JexlBuilder.html">JexlBuilder</a> and
+ <a href="apidocs/org/apache/commons/jexl3/scripting/JexlScriptEngine.html">JexlScriptEngine</a> ) or use this
+ opportunity to reduce exposure. Whether Files, URLs, networking, processes, class-loaders or reflection features are
+ accessible are part of the choice to make.</p><p>
+ <strong>Any CVE linked to JEXL should try resolution by first upgrading to JEXL 3.3.</strong>
+ </p>
+ </section>
+ <section name="What's new in 3.3:">
+ <p>
+ JEXL 3.3 brings the ability to configure permissions on libraries in the manner pioneered
+ with the @NoJexl annotation on source code. This is achieved through a crude but light mechanism akin to
+ a security manager that controls what JEXL can introspect and thus expose to scripts.
+ </p><p>
+ Used in conjunction with options (JexlOptions) and features (JexlFeatures), the permissions (JexlPermissions)
+ allow fine-tuning the scripting integration into any project.
+ </p><p>
+ JEXL 3.3 also adds some syntactic (ECMAScript) features (let, const, =>, for, ...) to further reduce
+ the skill set required to write scripts.
+ </p>
+ </section>
+
+ <section name="New Features in 3.3:">
+ <p>
+ <table>
+ <tr><td>JEXL-392:</td><td>Enable namespace declaration based on scripts</td></tr>
+ <tr><td>JEXL-391:</td><td>Improve in/=~ operator when arguments are arrays and collections</td></tr>
+ <tr><td>JEXL-390:</td><td>Pragmas should not be statements</td></tr>
+ <tr><td>JEXL-389:</td><td>Improve parsing timings</td></tr>
+ <tr><td>JEXL-385:</td><td>Support disabling fortran-style relational operators syntax</td></tr>
+ <tr><td>JEXL-382:</td><td>Simplify grammar and lexical state management</td></tr>
+ <tr><td>JEXL-380:</td><td>Multiple values per pragma key</td></tr>
+ <tr><td>JEXL-379:</td><td>Allow new to use class identifier</td></tr>
+ <tr><td>JEXL-373:</td><td>Add support for prefix/postfix increment/decrement operators</td></tr>
+ <tr><td>JEXL-372:</td><td>Add support for 'standard' for loop</td></tr>
+ <tr><td>JEXL-369:</td><td>Add 'let' and 'const' variable declarations</td></tr>
+ <tr><td>JEXL-367:</td><td>Named function and fat-arrow (=>) lambda syntax</td></tr>
+ <tr><td>JEXL-366:</td><td>Fail to evaluate string and number comparison</td></tr>
+ <tr><td>JEXL-365:</td><td>Lambda expressions</td></tr>
+ <tr><td>JEXL-363:</td><td>Allow retrieving captured variables in script</td></tr>
+ <tr><td>JEXL-360:</td><td>Add missing bitshift operators ( >>>, >>, <<)</td></tr>
+ <tr><td>JEXL-359:</td><td>Allow per-operator arithmetic handling of null arguments</td></tr>
+ <tr><td>JEXL-357:</td><td>Configure accessible packages/classes/methods/fields</td></tr>
+ </table>
+ </p>
+ </section>
+
+ <section name="Bugs Fixed in 3.3:">
+ <p>
+ <table>
+ <tr><td>JEXL-386:</td><td>Non-inheritable permissions on interfaces are ignored in an inheritable sandbox</td></tr>
+ <tr><td>JEXL-384:</td><td>Improve control over JexlArithmetic null argument handling</td></tr>
+ <tr><td>JEXL-378:</td><td>Incremental operator and decremental operator do not honor the side-effect flag</td></tr>
+ <tr><td>JEXL-376:</td><td>Introspector captures methods on non-exported classes (modules, java9+)</td></tr>
+ <tr><td>JEXL-375:</td><td>Cannot access enums by their name when using sandbox</td></tr>
+ <tr><td>JEXL-374:</td><td>No exception if dereferencing null object using safe(false) and antish(false)</td></tr>
+ <tr><td>JEXL-371:</td><td>Override of a protected method with public visibility is not callable</td></tr>
+ <tr><td>JEXL-370:</td><td>Cannot check if variable is defined using ObjectContext if the value is null</td></tr>
+ <tr><td>JEXL-368:</td><td>Namespace functor resolution is not cached</td></tr>
+ <tr><td>JEXL-364:</td><td>Evaluator options not propagated in closures</td></tr>
+ <tr><td>JEXL-362:</td><td>JexlInfo position reporting is off</td></tr>
+ <tr><td>JEXL-361:</td><td>Null may be used as operand silently even in arithmetic strict(true) mode</td></tr>
+ <tr><td>JEXL-354:</td><td>#pragma does not handle negative integer or real literals</td></tr>
+ <tr><td>JEXL-353:</td><td>Documentation error for not-in/not-match operator</td></tr>
+ </table>
+ </p>
+ </section>
+ </body>
+</document>
diff --git a/src/test/java/org/apache/commons/jexl3/ClassPermissions.java b/src/test/java/org/apache/commons/jexl3/ClassPermissions.java
deleted file mode 100644
index 0484dfe5..00000000
--- a/src/test/java/org/apache/commons/jexl3/ClassPermissions.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.jexl3;
-
-import org.apache.commons.jexl3.introspection.JexlPermissions;
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * An example of permission delegation that augments the RESTRICTED permission with an explicit
- * set of classes.
- * <p>Typical use case is to deny access to a package - and thus all its classes - but allow
- * a few specific classes.</p>
- */
-public class ClassPermissions extends JexlPermissions.Delegate {
- /** The set of explicitly allowed classes, overriding the delegate permissions. */
- private final Set<String> allowedClasses;
-
- /**
- * Creates permissions based on the RESTRICTED set but allowing an explicit set.
- * @param allow the set of allowed classes
- */
- public ClassPermissions(Class... allow) {
- this(JexlPermissions.RESTRICTED,
- Arrays.asList(Objects.requireNonNull(allow))
- .stream().map(Class::getCanonicalName) .collect(Collectors.toList()));
- }
-
- /**
- * Required for compose().
- * @param delegate the base to delegate to
- * @param allow the list of class canonical names
- */
- public ClassPermissions(JexlPermissions delegate, Collection<String> allow) {
- super(delegate);
- allowedClasses = new HashSet<>(Objects.requireNonNull(allow));
- }
-
- private boolean isClassAllowed(Class<?> clazz) {
- return allowedClasses.contains(clazz.getCanonicalName());
- }
-
- @Override
- public boolean allow(Class<?> clazz) {
- return (validate(clazz) && isClassAllowed(clazz)) || super.allow(clazz);
- }
-
- @Override
- public boolean allow(Method method) {
- return (validate(method) && isClassAllowed(method.getDeclaringClass())) || super.allow(method);
- }
-
- @Override
- public JexlPermissions compose(String... src) {
- return new ClassPermissions(base.compose(src), allowedClasses);
- }
-}
diff --git a/src/test/java/org/apache/commons/jexl3/examples/StreamTest.java b/src/test/java/org/apache/commons/jexl3/examples/StreamTest.java
index e5fb7fe2..57ee335a 100644
--- a/src/test/java/org/apache/commons/jexl3/examples/StreamTest.java
+++ b/src/test/java/org/apache/commons/jexl3/examples/StreamTest.java
@@ -16,8 +16,6 @@
*/
package org.apache.commons.jexl3.examples;
-import org.apache.commons.jexl3.ClassPermissions;
-import org.apache.commons.jexl3.JexlArithmetic;
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.jexl3.JexlEngine;
@@ -25,6 +23,7 @@ import org.apache.commons.jexl3.JexlFeatures;
import org.apache.commons.jexl3.JexlScript;
import org.apache.commons.jexl3.MapContext;
import org.apache.commons.jexl3.introspection.JexlPermissions;
+import org.apache.commons.jexl3.introspection.JexlPermissions.ClassPermissions;
import org.junit.Assert;
import org.junit.Test;
diff --git a/src/test/java/org/apache/commons/jexl3/scripting/JexlScriptEngineTest.java b/src/test/java/org/apache/commons/jexl3/scripting/JexlScriptEngineTest.java
index e119eb65..9a38936c 100644
--- a/src/test/java/org/apache/commons/jexl3/scripting/JexlScriptEngineTest.java
+++ b/src/test/java/org/apache/commons/jexl3/scripting/JexlScriptEngineTest.java
@@ -55,6 +55,7 @@ public class JexlScriptEngineTest {
public void tearDown() {
JexlBuilder.setDefaultPermissions(null);
JexlScriptEngine.setInstance(null);
+ JexlScriptEngine.setPermissions(null);
}
@Test
@@ -125,12 +126,8 @@ public class JexlScriptEngineTest {
@Test
public void testScriptingInstance0() throws Exception {
+ JexlScriptEngine.setPermissions(JexlPermissions.UNRESTRICTED);
final ScriptEngineManager manager = new ScriptEngineManager();
- JexlScriptEngine.setInstance(new JexlBuilder()
- .cache(512)
- .logger(LogFactory.getLog(JexlScriptEngine.class))
- .permissions(JexlPermissions.UNRESTRICTED)
- .create());
final ScriptEngine engine = manager.getEngineByName("jexl3");
Long time2 = (Long) engine.eval(
"sys=context.class.forName(\"java.lang.System\");"
@@ -141,6 +138,7 @@ public class JexlScriptEngineTest {
@Test
public void testScriptingPermissions1() throws Exception {
JexlBuilder.setDefaultPermissions(JexlPermissions.UNRESTRICTED);
+ JexlScriptEngine.setPermissions(null);
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("jexl3");
Long time2 = (Long) engine.eval(