This is an automated email from the ASF dual-hosted git repository.
henrib pushed a commit to branch JEXL-458
in repository https://gitbox.apache.org/repos/asf/commons-jexl.git
The following commit(s) were added to refs/heads/JEXL-458 by this push:
new e01612be JEXL-458: added +/- syntax for permission package parsing; -
refined ability to specify and correctly apply class permissions; - added new
JexlPermissions methods (allow(class, method), allow(class, field); - use
specific test permissions in tests (sic);
e01612be is described below
commit e01612be0d4c441ad13b2f41e1c66393c8e0f4ad
Author: Henrib <[email protected]>
AuthorDate: Tue Apr 21 22:01:43 2026 +0200
JEXL-458: added +/- syntax for permission package parsing;
- refined ability to specify and correctly apply class permissions;
- added new JexlPermissions methods (allow(class, method), allow(class,
field);
- use specific test permissions in tests (sic);
---
RELEASE-NOTES.txt | 3 +-
pom.xml | 2 +-
src/changes/changes.xml | 1 +
.../java/org/apache/commons/jexl3/JexlBuilder.java | 6 +-
.../jexl3/introspection/JexlPermissions.java | 196 +++++++-----
.../commons/jexl3/scripting/JexlScriptEngine.java | 20 +-
.../jexl3/{JexlTest.java => ExpressionsTest.java} | 6 +-
src/test/java/org/apache/commons/jexl3/Foo.java | 5 +-
.../org/apache/commons/jexl3/Issues300Test.java | 15 +-
.../org/apache/commons/jexl3/Issues400Test.java | 35 ++-
.../java/org/apache/commons/jexl3/JXLTTest.java | 4 +-
.../org/apache/commons/jexl3/JexlTestCase.java | 343 ++++++++-------------
.../java/org/apache/commons/jexl3/SwitchTest.java | 3 +-
.../apache/commons/jexl3/examples/StreamTest.java | 30 +-
.../internal/introspection/DiscoveryTest.java | 16 +-
.../internal/introspection/PermissionsTest.java | 15 +-
.../scripting/JexlScriptEngineOptionalTest.java | 8 +
.../jexl3/scripting/JexlScriptEngineTest.java | 29 +-
18 files changed, 385 insertions(+), 352 deletions(-)
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index bd0acd5b..f760c168 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -14,8 +14,9 @@ This is a feature and maintenance release. Java 8 or later is
required.
Bugs Fixed in 3.6.3:
====================
+o JEXL-458: Improve permissions expressivity.
+o JEXL-457: Reduce default exposure for RESTRICTED JexlPermissions.
o JEXL-456: Change in template parser behavior.
-o JEXL-457: Reduce default exposure for RESTRICTED JexlPermissions
Changes in 3.6.3:
diff --git a/pom.xml b/pom.xml
index 87a95b80..f0eb9261 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
<parent>
<groupId>org.apache.commons</groupId>
<artifactId>commons-parent</artifactId>
- <version>97</version>
+ <version>98</version>
</parent>
<artifactId>commons-jexl3</artifactId>
<version>3.6.3-SNAPSHOT</version>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index ed6bf379..bbdb1200 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-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>
<!-- ADD -->
diff --git a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
index fc826318..66b1d8df 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
@@ -87,7 +87,7 @@ public class JexlBuilder {
/**
* The set of default permissions used when creating a new builder.
- * <p>Static but modifiable so these default permissions can be changed to
a purposeful set.</p>
+ * <p>Static but modifiable, so these default permissions can be changed
to a purposeful set.</p>
* <p>In JEXL 3.3, these are {@link JexlPermissions#RESTRICTED}.</p>
* <p>In JEXL 3.2, these were equivalent to {@link
JexlPermissions#UNRESTRICTED}.</p>
*/
@@ -167,7 +167,7 @@ public class JexlBuilder {
* and methods are visible to JEXL and usable in scripts using default
implicit behaviors.
* </p><p>
* However, without mitigation, this change will likely break some scripts
at runtime, especially those exposing
- * your own class instances through arguments, contexts or namespaces.
+ * your own class instances through arguments, contexts, or namespaces.
* The new default set of allowed packages and denied classes is described
by {@link JexlPermissions#RESTRICTED}.
* </p><p>
* The recommended mitigation if your usage of JEXL is impacted is to
first thoroughly review what should be
@@ -550,7 +550,7 @@ public class JexlBuilder {
/**
* Sets whether the engine is in lexical shading mode.
*
- * @param flag true means lexical shading is in effect, false implies no
lexical shading
+ * @param flag true means lexical shading is in effect; false implies no
lexical shading
* @return this builder
* @since 3.2
*/
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 8a944cee..e5748476 100644
--- a/src/main/java/org/apache/commons/jexl3/introspection/JexlPermissions.java
+++ b/src/main/java/org/apache/commons/jexl3/introspection/JexlPermissions.java
@@ -44,7 +44,27 @@ import
org.apache.commons.jexl3.internal.introspection.PermissionsParser;
* implementations shall delegate calls to its methods for {@link
org.apache.commons.jexl3.annotations.NoJexl} to be
* processed.</p>
* <p>A simple textual configuration can be used to create user-defined
permissions using
- * {@link JexlPermissions#parse(String...)}.</p>
+ * {@link JexlPermissions#parse(String...)}. The permission syntax supports
both positive (+) and negative (-)
+ * declarations:</p>
+ * <ul>
+ * <li><b>Negative restrictions ({@code -})</b>: By default or when prefixed
with {@code -}, class restrictions
+ * explicitly <b>deny</b> access to the specified members (or the entire class
if the block is empty).
+ * This is the default mode and works like {@link
org.apache.commons.jexl3.annotations.NoJexl}.</li>
+ * <li><b>Positive restrictions ({@code +})</b>: When prefixed with {@code +},
class restrictions
+ * explicitly <b>allow only</b> the specified members (or the entire class if
the block is empty), denying
+ * all others. This provides a whitelist approach where you must explicitly
list what is permitted.</li>
+ * </ul>
+ * <p>For example:</p>
+ * <pre>
+ * // Deny specific methods in a class (negative restriction - default)
+ * java.lang { System { exit(); } } // or -System { exit(); }
+ *
+ * // Allow only specific methods in a class (positive restriction)
+ * java.lang { +System { currentTimeMillis(); nanoTime(); } }
+ *
+ * // Allow entire class (positive restriction with empty block)
+ * java.io -{ +PrintWriter{} +Writer{} }
+ * </pre>
*
*<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
@@ -74,63 +94,79 @@ public interface JexlPermissions {
* <p>Note that the newer positive restriction syntax is preferable as in:
* <code>RESTRICTED.compose("java.lang { +Class {} }")</code>.</p>
*/
- final class ClassPermissions extends JexlPermissions.Delegate {
+ final 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(final Class<?>... allow) {
+ this(JexlPermissions.RESTRICTED, allow);
+ }
- /** The set of explicitly allowed classes, overriding the delegate
permissions. */
- private final Set<String> allowedClasses;
+ public ClassPermissions(final JexlPermissions permissions, final
Class<?>... allow) {
+ this(permissions,
Arrays.stream(Objects.requireNonNull(allow)).map(Class::getCanonicalName).collect(Collectors.toList()));
+ }
- /**
- * Creates permissions based on the RESTRICTED set but allowing an
explicit set.
- *
- * @param allow the set of allowed classes
- */
- public ClassPermissions(final Class<?>... allow) {
- this(JexlPermissions.RESTRICTED,
- Arrays.stream(Objects.requireNonNull(allow))
- .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(final JexlPermissions delegate, final
Collection<String> allow) {
+ super(Objects.requireNonNull(delegate));
+ allowedClasses = new HashSet<>(Objects.requireNonNull(allow));
+ }
- /**
- * Required for compose().
- *
- * @param delegate the base to delegate to
- * @param allow the list of class canonical names
- */
- public ClassPermissions(final JexlPermissions delegate, final
Collection<String> allow) {
- super(Objects.requireNonNull(delegate));
- allowedClasses = new HashSet<>(Objects.requireNonNull(allow));
- }
+ @Override
+ public boolean allow(final Constructor<?> constructor) {
+ return validate(constructor) &&
+
(allowedClasses.contains(constructor.getDeclaringClass().getCanonicalName()) ||
super.allow(constructor));
+ }
- @Override
- public boolean allow(final Class<?> clazz) {
- return validate(clazz) && (isClassAllowed(clazz) ||
super.allow(clazz));
- }
+ @Override
+ public boolean allow(final Class<?> clazz) {
+ return validate(clazz) &&
allowedClasses.contains(clazz.getCanonicalName()) || super.allow(clazz);
+ }
- @Override
- public boolean allow(final Constructor<?> constructor) {
- return validate(constructor) &&
-
(allowedClasses.contains(constructor.getDeclaringClass().getCanonicalName()) ||
super.allow(constructor));
+ @Override
+ public boolean allow(final Class<?> clazz, final Field field) {
+ if (!validate(field)) {
+ return false;
}
-
- @Override
- public boolean allow(final Class<?> clazz, final Method method) {
- if (!validate(method)) {
- return false;
- }
- if (!method.getDeclaringClass().isAssignableFrom(clazz)) {
- return false;
- }
- if (super.allow(clazz, method)) {
- return true;
- }
- return isClassAllowed(clazz);
+ if (!clazz.isAssignableFrom(field.getDeclaringClass())) {
+ return false;
}
+ if (super.allow(clazz, field)) {
+ return true;
+ }
+ return isClassAllowed(clazz);
+ }
- @Override
- public JexlPermissions compose(final String... src) {
- return new ClassPermissions(base.compose(src), allowedClasses);
+ @Override
+ public boolean allow(final Class<?> clazz, final Method method) {
+ if (!validate(method)) {
+ return false;
+ }
+ if (!method.getDeclaringClass().isAssignableFrom(clazz)) {
+ return false;
+ }
+ if (super.allow(clazz, method)) {
+ return true;
}
+ return isClassAllowed(clazz);
+ }
+
+ @Override
+ public JexlPermissions compose(final String... src) {
+ return new ClassPermissions(base.compose(src), allowedClasses);
+ }
private boolean isClassAllowed(final Class<?> aClass) {
Class<?> clazz = aClass;
@@ -155,9 +191,10 @@ public interface JexlPermissions {
* A base for permission delegation allowing functional refinement.
* Overloads should call the appropriate validate() method early in their
body.
*/
- class Delegate implements JexlPermissions {
-
- /** The permissions we delegate to. */
+ class Delegate implements JexlPermissions {
+ /**
+ * The permissions we delegate to.
+ */
protected final JexlPermissions base;
/**
@@ -181,12 +218,22 @@ public interface JexlPermissions {
@Override
public boolean allow(final Field field) {
- return base.allow(field);
+ return validate(field) && allow(field.getDeclaringClass(), field);
+ }
+
+ @Override
+ public boolean allow(final Class<?> clazz, final Field field) {
+ return base.allow(clazz, field);
}
@Override
public boolean allow(final Method method) {
- return base.allow(method);
+ return validate(method) && allow(method.getDeclaringClass(),
method);
+ }
+
+ @Override
+ public boolean allow(final Class<?> clazz, final Method method) {
+ return base.allow(clazz, method);
}
@Override
@@ -240,7 +287,7 @@ public interface JexlPermissions {
* </ul>
*/
- JexlPermissions DEFAULT = JexlPermissions.parse(
+ JexlPermissions RESTRICTED = JexlPermissions.parse(
"# Default Uberspect Permissions",
"java.math.*",
"java.text.*",
@@ -254,15 +301,8 @@ public interface JexlPermissions {
"}",
"java.io -{ +PrintWriter{} +Writer{} +StringWriter{} +Reader{}
+InputStream{} +OutputStream{} }",
"java.nio +{}",
- "java.nio.charset +{}"
- );
- JexlPermissions RESTRICTED = DEFAULT.compose(
- // the following ones are mostly for tests, these should be reassessed
(jxlt)
- "org.apache.commons.jexl3.*",
- "org.apache.commons.jexl3 +{ -JexlBuilder{} }",
- "org.apache.commons.jexl3.introspection -{ JexlPermissions{}
JexlPermissions$ClassPermissions{} }",
- "org.apache.commons.jexl3.internal -{ Engine{} Engine32{}
TemplateEngine{} }",
- "org.apache.commons.jexl3.internal.introspection -{ Uberspect{}
Introspector{} }"
+ "java.nio.charset +{}",
+ "org.apache.commons.jexl3 +{ -JexlBuilder{} }"
);
/**
@@ -290,6 +330,7 @@ public interface JexlPermissions {
* java.lang { Runtime {} System {} ProcessBuilder {} Class {} }
* org.apache.commons.jexl3 { JexlBuilder {} }
* </pre>
+ * <p><b>Syntax Overview:</b></p>
* <ul>
* <li>Syntax for wildcards is the name of the package suffixed by {@code
.*}.</li>
* <li>Syntax for restrictions is a list of package restrictions.</li>
@@ -301,26 +342,36 @@ public interface JexlPermissions {
* nested classes -, a field which is the Java field name suffixed with
{@code ;}, a method composed of
* its Java name suffixed with {@code ();}. Constructor restrictions are
specified like methods using the
* class name as method name.</li>
- * <li>By default or when prefixed with a {@code -}, a class restriction
is explicitly denying the members
- * declared in its block (or the whole class)</li>
- * <li>When prefixed with a {@code +}, a class restriction is explicitly
allowing the members
- * declared in its block (or the whole class)</li>
+ * </ul>
+ * <p><b>Negative ({@code -}) vs Positive ({@code +})
Restrictions:</b></p>
+ * <ul>
+ * <li><b>Negative restriction (default or {@code -} prefix)</b>:
Explicitly <b>denies</b> access to the members
+ * declared in its block. If the block is empty, the entire class is
denied.
+ * <br>Example: {@code java.lang { -System { exit(); } }} denies
System.exit() but allows other System methods.
+ * <br>Example: {@code java.lang { Runtime {} }} denies the entire
Runtime class (empty block means deny all).</li>
+ * <li><b>Positive restriction ({@code +} prefix)</b>: Explicitly
<b>allows only</b> the members declared
+ * in its block, denying all others not listed. If the block is empty,
the entire class is allowed.
+ * <br>Example: {@code java.lang { +System { currentTimeMillis(); } }}
allows only System.currentTimeMillis(),
+ * denying all other System methods.
+ * <br>Example: {@code java.io -{ +PrintWriter{} +Writer{} }} in the
context of a denied java.io package,
+ * allows only PrintWriter and Writer classes entirely (empty blocks mean
allow all members).</li>
* </ul>
* <p>
- * All overrides and overloads of a constructors or method are allowed or
restricted at the same time,
+ * All overrides and overloads of constructors or methods are allowed or
restricted at the same time,
* the restriction being based on their names, not their whole signature.
This differs from the @NoJexl annotation.
* </p>
+ * <p><b>Complete Example:</b></p>
* <pre>
* # some wildcards
- * java.lang.*; # java.lang is pretty much a must have
+ * java.util.* # java.util is pretty much a must-have
* my.allowed.package0.*
* another.allowed.package1.*
* # nojexl like restrictions
* my.package.internal {} # the whole package is hidden
* my.package {
- * +class4 { theMethod(); } # only theMethod can be called in class4
+ * +class4 { theMethod(); } # POSITIVE: only theMethod can be called in
class4, all others denied
* class0 {
- * class1 {} # the whole class1 is hidden
+ * class1 {} # NEGATIVE (default): the whole class1 is hidden
* class2 {
* class2(); # class2 constructors cannot be invoked
* class3 {
@@ -407,6 +458,7 @@ public interface JexlPermissions {
* <p>Since methods can be overridden and overloaded, this checks that
this class explicitly allows
* this method - superseding any superclass or interface specified
permissions.</p>
*
+ * @param clazz the class from which the method is accessed, used to check
that the method is allowed for this class
* @param method the method to check
* @return true if JEXL is allowed to introspect, false otherwise
* @since 3.7
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 52c9db7e..f9bc2791 100644
--- a/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngine.java
+++ b/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngine.java
@@ -184,8 +184,10 @@ public class JexlScriptEngine extends AbstractScriptEngine
implements Compilable
*/
public class JexlScriptObject {
- /** Default constructor */
- public JexlScriptObject() {} // Keep Javadoc happy
+ /** Default constructor. */
+ public JexlScriptObject() {
+ // Keep Javadoc happy
+ }
/**
* Gives access to the underlying JEXL engine shared between all
ScriptEngine instances.
@@ -296,9 +298,11 @@ public class JexlScriptEngine extends AbstractScriptEngine
implements Compilable
.safe(false)
.logger(JexlScriptEngine.LOG)
.cache(JexlScriptEngine.CACHE_SIZE);
- if (PERMISSIONS != null) {
- builder.permissions(PERMISSIONS);
- }
+ // ensure the script object class is always allowed for
all permissions set
+ JexlPermissions permissions = new
JexlPermissions.ClassPermissions(
+ PERMISSIONS == null ? JexlPermissions.RESTRICTED :
PERMISSIONS,
+ JexlScriptObject.class);
+ builder.permissions(permissions);
engine = builder.create();
ENGINE = new SoftReference<>(engine);
}
@@ -442,15 +446,15 @@ public class JexlScriptEngine extends
AbstractScriptEngine implements Compilable
public Object eval(final Reader reader, final ScriptContext context)
throws ScriptException {
// This is mandated by JSR-223 (see SCR.5.5.2 Methods)
Objects.requireNonNull(reader, "reader");
- Objects.requireNonNull(context, "context");
+ Objects.requireNonNull(context, CONTEXT_KEY);
return eval(readerToString(reader), context);
}
@Override
public Object eval(final String script, final ScriptContext context)
throws ScriptException {
// This is mandated by JSR-223 (see SCR.5.5.2 Methods)
- Objects.requireNonNull(script, "context");
- Objects.requireNonNull(context, "context");
+ Objects.requireNonNull(script, CONTEXT_KEY);
+ Objects.requireNonNull(context, CONTEXT_KEY);
// This is mandated by JSR-223 (end of section SCR.4.3.4.1.2 -
JexlScript Execution)
context.setAttribute(CONTEXT_KEY, context, ScriptContext.ENGINE_SCOPE);
try {
diff --git a/src/test/java/org/apache/commons/jexl3/JexlTest.java
b/src/test/java/org/apache/commons/jexl3/ExpressionsTest.java
similarity index 99%
rename from src/test/java/org/apache/commons/jexl3/JexlTest.java
rename to src/test/java/org/apache/commons/jexl3/ExpressionsTest.java
index 35de2b5b..b90e784b 100644
--- a/src/test/java/org/apache/commons/jexl3/JexlTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ExpressionsTest.java
@@ -43,7 +43,7 @@ import org.junit.jupiter.api.Test;
* Simple test cases.
*/
@SuppressWarnings({"UnnecessaryBoxing",
"AssertEqualsBetweenInconvertibleTypes"})
-public final class JexlTest extends JexlTestCase {
+public final class ExpressionsTest extends JexlTestCase {
public static final class Duck {
int user = 10;
@@ -79,8 +79,8 @@ public final class JexlTest extends JexlTestCase {
static final String GET_METHOD_STRING = "GetMethod string";
- public JexlTest() {
- super("JexlTest",
+ public ExpressionsTest() {
+ super("ExpressionsTest",
new JexlBuilder()
.strict(false)
.imports(Arrays.asList("java.lang","java.math"))
diff --git a/src/test/java/org/apache/commons/jexl3/Foo.java
b/src/test/java/org/apache/commons/jexl3/Foo.java
index f1b3ad24..a3d96cf6 100644
--- a/src/test/java/org/apache/commons/jexl3/Foo.java
+++ b/src/test/java/org/apache/commons/jexl3/Foo.java
@@ -34,11 +34,10 @@ public class Foo {
}
private boolean beenModified;
private String property1 = "some value";
- public Foo() {}
public String bar()
{
- return JexlTest.METHOD_STRING;
+ return ExpressionsTest.METHOD_STRING;
}
public String convertBoolean(final boolean b)
@@ -58,7 +57,7 @@ public class Foo {
public String getBar()
{
- return JexlTest.GET_METHOD_STRING;
+ return ExpressionsTest.GET_METHOD_STRING;
}
public List<String> getCheeseList()
diff --git a/src/test/java/org/apache/commons/jexl3/Issues300Test.java
b/src/test/java/org/apache/commons/jexl3/Issues300Test.java
index f9879567..a56efdf6 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues300Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues300Test.java
@@ -17,7 +17,7 @@
package org.apache.commons.jexl3;
import static org.apache.commons.jexl3.internal.Util.debuggerCheck;
-import static
org.apache.commons.jexl3.introspection.JexlPermissions.RESTRICTED;
+import static org.apache.commons.jexl3.JexlTestCase.TEST_PERMS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
@@ -48,6 +48,7 @@ import java.util.stream.Stream;
import org.apache.commons.jexl3.internal.Engine32;
import org.apache.commons.jexl3.internal.OptionsContext;
import org.apache.commons.jexl3.introspection.JexlSandbox;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
@@ -62,11 +63,11 @@ class Issues300Test {
@Override
public boolean isStrict(final JexlOperator op) {
switch (op) {
- case NOT:
- case CONDITION:
- return false;
+ case NOT:
+ case CONDITION:
+ return false;
+ default: return super.isStrict(op);
}
- return super.isStrict(op);
}
}
@@ -777,7 +778,9 @@ class Issues300Test {
final String text = "(A ? C.D : E)";
final JexlEngine jexl = new JexlBuilder().safe(true).create();
final JexlExpression expr = jexl.createExpression(text);
+ Assertions.assertNotNull(expr);
final JexlScript script = jexl.createScript(text);
+ Assertions.assertNotNull(script);
}
@Test
@@ -1143,7 +1146,7 @@ class Issues300Test {
void testIssue397() {
String result;
final String control = Class397.class.getName();
- final JexlEngine jexl = new
JexlBuilder().permissions(RESTRICTED).create();
+ final JexlEngine jexl = new
JexlBuilder().permissions(TEST_PERMS).create();
final Interface397i instance = new Class397();
result = (String) jexl.invokeMethod(instance, "summary");
diff --git a/src/test/java/org/apache/commons/jexl3/Issues400Test.java
b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
index 119385d4..cf366c81 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues400Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
@@ -54,6 +54,7 @@ import org.apache.commons.jexl3.parser.JexlScriptParser;
import org.apache.commons.jexl3.parser.Parser;
import org.apache.commons.jexl3.parser.StringProvider;
import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
@@ -115,6 +116,11 @@ public class Issues400Test {
assertEquals(42, result);
}
+ @BeforeAll
+ static void setUpClass() {
+ JexlBuilder.setDefaultPermissions(JexlTestCase.TEST_PERMS);
+ }
+
@Test
void test402() {
final JexlContext jc = new MapContext();
@@ -876,7 +882,7 @@ public class Issues400Test {
// need to explicitly allow Uberspect and the current class loader to
load System
Class<? extends ClassLoader> cloader =
getClass().getClassLoader().getClass();
JexlPermissions perm = new JexlPermissions.ClassPermissions(
- ClassLoader.class,
+ getClass().getClassLoader().getClass(),
org.apache.commons.jexl3.internal.introspection.Uberspect.class);
//assertTrue(perm.allow(cloader), "should be able to access
ClassLoader");
assertTrue(perm.allow(cloader, cloader.getMethod("loadClass",
String.class)), "should be able to access ClassLoader::loadClass");
@@ -898,8 +904,11 @@ public class Issues400Test {
// need explicit permissions to ClassPermissions and Uberspect to
reach and invoke
// System::currentTimeMillis
JexlPermissions perm = new JexlPermissions.ClassPermissions(
+ getClass().getClassLoader().getClass(),
JexlPermissions.ClassPermissions.class,
- org.apache.commons.jexl3.internal.introspection.Uberspect.class);
+ org.apache.commons.jexl3.internal.introspection.Uberspect.class,
+
org.apache.commons.jexl3.internal.introspection.MethodExecutor.class,
+ BrkContext.class);
assertNotNull(run450c(perm));
// cannot reach and invoke System::currentTimeMillis with RESTRICTED
assertThrows(JxltEngine.Exception.class, () ->
run450c(JexlPermissions.RESTRICTED),
@@ -915,17 +924,21 @@ public class Issues400Test {
+ "sys = x?.getClassLoader()?.loadClass('java.lang.System') ?:
SYSTEM;"
+ "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);}")
+ + "z =
brk(c.getMethod(sys,'currentTimeMillis')).invoke(x,null);}")
.evaluate(new BrkContext());
return result;
}
@Test
- void test450() {
+ void test450() throws Exception {
+ JexlPermissions perms = Engine33.createPermissions();
+ Class<? extends ClassLoader> cl =
Issues400Test.class.getClassLoader().getClass();
+ assertTrue(perms.allow(cl, cl.getMethod("loadClass", String.class)),
+ "should be able to access ClassLoader::loadClass with specific
permissions");
assertNotNull(run450(JexlPermissions.UNRESTRICTED),
"should be able to reach and invoke System::currentTimeMillis with
UNRESTRICTED");
assertNotNull(
- run450(new JexlPermissions.ClassPermissions(
+ run450(new JexlPermissions.ClassPermissions(perms,
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),
@@ -941,12 +954,18 @@ public class Issues400Test {
super(builder);
}
- static JexlBuilder createBuilder() {
- JexlPermissions perm = new JexlPermissions.ClassPermissions(
- Issues400Test.class.getClassLoader().getClass(),
+ static JexlPermissions createPermissions() {
+ // Need a lot of things on top of JEXL37 to allow execution
+ return new
JexlPermissions.ClassPermissions(JexlTestCase.TEST_PERMS,
+ Engine33.class.getClassLoader().getClass(),
+ Engine33.class,
JexlPermissions.ClassPermissions.class,
org.apache.commons.jexl3.internal.TemplateEngine.class,
org.apache.commons.jexl3.internal.introspection.Uberspect.class);
+ }
+
+ static JexlBuilder createBuilder() {
+ JexlPermissions perm = createPermissions();
return new
JexlBuilder().safe(false).silent(false).permissions(perm);
}
}
diff --git a/src/test/java/org/apache/commons/jexl3/JXLTTest.java
b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
index fe9515f1..803d1207 100644
--- a/src/test/java/org/apache/commons/jexl3/JXLTTest.java
+++ b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
@@ -58,8 +58,6 @@ import org.junit.jupiter.params.provider.MethodSource;
*/
@SuppressWarnings({"UnnecessaryBoxing",
"AssertEqualsBetweenInconvertibleTypes"})
class JXLTTest extends JexlTestCase {
- static JexlPermissions P0 = JexlPermissions.DEFAULT;
- static JexlPermissions P1 = JexlPermissions.RESTRICTED;
public static class Context311 extends MapContext
implements JexlContext.OptionsHandle, JexlContext.ThreadLocal {
@@ -150,7 +148,7 @@ class JXLTTest extends JexlTestCase {
public static List<JexlBuilder> engines() {
final JexlFeatures f = new JexlFeatures();
f.lexical(true).lexicalShade(true);
- JexlPermissions permissions =
JexlPermissions.DEFAULT.compose("org.apache.commons.jexl3 +{ JXLTTest{} }");
+ JexlPermissions permissions =
JexlPermissions.RESTRICTED.compose("org.apache.commons.jexl3 +{ JXLTTest{} }");
return Arrays.asList(
new
JexlBuilder().permissions(permissions).silent(false).lexical(true).lexicalShade(true).cache(128).strict(true),
new
JexlBuilder().permissions(permissions).features(f).silent(false).cache(128).strict(true),
diff --git a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
index 246be186..e9b3b9a3 100644
--- a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
+++ b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
@@ -17,19 +17,15 @@
package org.apache.commons.jexl3;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.fail;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-
import org.apache.commons.jexl3.internal.Debugger;
import org.apache.commons.jexl3.internal.OptionsContext;
import org.apache.commons.jexl3.internal.Util;
import org.apache.commons.jexl3.internal.introspection.Uberspect;
import org.apache.commons.jexl3.introspection.JexlPermissions;
import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+
+import java.util.Arrays;
/**
* Implements runTest methods to dynamically instantiate and invoke a test,
@@ -37,206 +33,135 @@ import org.junit.jupiter.api.AfterEach;
* Eases the implementation of main methods to debug.
*/
public class JexlTestCase {
- public static class PragmaticContext extends OptionsContext implements
JexlContext.PragmaProcessor, JexlContext.OptionsHandle {
- private final JexlOptions options;
-
- public PragmaticContext() {
- this(new JexlOptions());
- }
-
- public PragmaticContext(final JexlOptions o) {
- this.options = o;
- }
-
- @Override
- public JexlOptions getEngineOptions() {
- return options;
- }
-
- @Override
- public void processPragma(final JexlOptions opts, final String key,
final Object value) {
- if ("script.mode".equals(key) && "pro50".equals(value)) {
- opts.set(MODE_PRO50);
- }
- }
-
- @Override
- public void processPragma(final String key, final Object value) {
- processPragma(null, key, value);
- }
- }
- // The default options: all tests where engine lexicality is
- // important can be identified by the builder calling lexical(...).
- static {
- JexlOptions.setDefaultFlags("-safe", "+lexical");
- }
-
- /** No parameters signature for test run. */
- private static final Class<?>[] NO_PARMS = {};
-
- /** String parameter signature for test run. */
- private static final Class<?>[] STRING_PARM = {String.class};
-
- // define mode pro50
- static final JexlOptions MODE_PRO50 = new JexlOptions();
- static {
- MODE_PRO50.setFlags("+strict +cancellable +lexical +lexicalShade
-safe".split(" "));
- }
-
- /**
- * A very secure singleton.
- */
- public static final JexlPermissions SECURE = JexlPermissions.RESTRICTED;
-
- public static final JexlPermissions TESTPERMS =
JexlPermissions.RESTRICTED.compose(
- "org.apache.commons.jexl3.*",
- "org.apache.commons.jexl3 +{ -JexlBuilder{} }",
- "org.apache.commons.jexl3.introspection -{ JexlPermissions{}
JexlPermissions$ClassPermissions{} }",
- "org.apache.commons.jexl3.internal -{ Engine{} Engine32{}
TemplateEngine{} }",
- "org.apache.commons.jexl3.internal.introspection -{ Uberspect{}
Introspector{} }");
-
- static JexlEngine createEngine() {
- return new JexlBuilder().create();
- }
-
- public static JexlEngine createEngine(final boolean lenient) {
- return createEngine(lenient, SECURE);
- }
- public static JexlEngine createEngine(final boolean lenient, final
JexlPermissions permissions) {
- return new JexlBuilder()
- .uberspect(new Uberspect(null, null, permissions))
- .arithmetic(new JexlArithmetic(!lenient)).cache(128).create();
- }
-
- static JexlEngine createEngine(final JexlFeatures features) {
- return new JexlBuilder().features(features).create();
- }
-
- /**
- * Will force testing the debugger for each derived test class by
- * recreating each expression from the JexlNode in the JexlEngine cache &
- * testing them for equality with the origin.
- * @throws Exception
- */
- public static void debuggerCheck(final JexlEngine ijexl) throws Exception {
- Util.debuggerCheck(ijexl);
- }
-
- /**
- * Compare strings ignoring white space differences.
- * <p>This replaces any sequence of whitespaces (ie \\s) by one space (ie
ASCII 32) in both
- * arguments then compares them.</p>
- * @param lhs left hand side
- * @param rhs right hand side
- * @return true if strings are equal besides whitespace
- */
- public static boolean equalsIgnoreWhiteSpace(final String lhs, final
String rhs) {
- final String lhsw = lhs.trim().replaceAll("\\s+", "");
- final String rhsw = rhs.trim().replaceAll("\\s+", "");
- return lhsw.equals(rhsw);
- }
-
- /**
- * Runs a test.
- * @param args where args[0] is the test class name and args[1] the test
class method
- * @throws Exception
- */
- public static void main(final String[] args) throws Exception {
- runTest(args[0], args[1]);
- }
-
- /**
- * Instantiate and runs a test method; useful for debugging purpose.
- * For instance:
- * <code>
- * public static void main(String[] args) throws Exception {
- * runTest("BitwiseOperatorTest","testAndVariableNumberCoercion");
- * }
- * </code>
- * @param tname the test class name
- * @param mname the test class method
- * @throws Exception
- */
- public static void runTest(final String tname, final String mname) throws
Exception {
- final String testClassName = "org.apache.commons.jexl3." + tname;
- final Class<JexlTestCase> clazz = null;
- JexlTestCase test = null;
- // find the class
- assertThrows(ClassNotFoundException.class, () ->
Class.forName(testClassName), () -> "no such class: " + testClassName);
- // find ctor & instantiate
- Constructor<JexlTestCase> ctor = null;
- try {
- ctor = clazz.getConstructor(STRING_PARM);
- test = ctor.newInstance("debug");
- } catch (final NoSuchMethodException xctor) {
- // instantiate default class ctor
- try {
- test = clazz.getConstructor().newInstance();
- } catch (final Exception xany) {
- fail("cant instantiate test: " + xany);
- return;
- }
- } catch (final Exception xany) {
- fail("cant instantiate test: " + xany);
- return;
- }
- // Run the test
- test.runTest(mname);
- }
-
- public static String toString(final JexlScript script) {
- final Debugger d = new Debugger().lineFeed("").indentation(0);
- d.debug(script);
- return d.toString();
- }
-
- /** A default JEXL engine instance. */
- protected final JexlEngine JEXL;
-
- public JexlTestCase(final String name) {
- this(name, new
JexlBuilder().imports(Arrays.asList("java.lang","java.math")).permissions(null).cache(128).create());
- }
-
- protected JexlTestCase(final String name, final JexlEngine jexl) {
- //super(name);
- JEXL = jexl;
- }
-
- /**
- * Dynamically runs a test method.
- * @param name the test method to run
- * @throws Exception if anything goes wrong
- */
- public void runTest(final String name) throws Exception {
- if ("runTest".equals(name)) {
- return;
- }
- Method method = null;
- try {
- method = this.getClass().getDeclaredMethod(name, NO_PARMS);
- }
- catch (final Exception xany) {
- fail("no such test: " + name);
- return;
- }
- try {
- setUp();
- method.invoke(this);
- } finally {
- tearDown();
- }
- }
-
- public void setUp() throws Exception {
- // nothing to do
- }
-
- public String simpleWhitespace(final String arg) {
- return arg.trim().replaceAll("\\s+", " ");
- }
-
- @AfterEach
- public void tearDown() throws Exception {
- debuggerCheck(JEXL);
- }
+ /**
+ * The restricted permissions that are used by default in JEXL tests.
+ * <p>Need to add jexl3 package but removes most internals</p>
+ */
+ public static final JexlPermissions TEST_PERMS =
JexlPermissions.RESTRICTED.compose(
+"org.apache.commons.jexl3.*",
+ "org.apache.commons.jexl3 +{ -JexlBuilder{} }",
+ "org.apache.commons.jexl3.introspection -{ JexlPermissions{}
JexlPermissions$ClassPermissions{} }",
+ "org.apache.commons.jexl3.internal -{ Engine{} Engine32{}
TemplateEngine{} }",
+ "org.apache.commons.jexl3.internal.introspection -{ Uberspect{}
Introspector{} }",
+ "java.net -{ +URI { -toURL() } }");
+
+ // define mode pro50
+ static final JexlOptions MODE_PRO50 = new JexlOptions();
+
+ // The default options: all tests where engine lexicality is
+ // important can be identified by the builder calling lexical(...).
+ static {
+ JexlBuilder.setDefaultPermissions(TEST_PERMS);
+ JexlOptions.setDefaultFlags("-safe", "+lexical");
+ MODE_PRO50.setFlags("+strict +cancellable +lexical +lexicalShade
-safe".split(" "));
+ }
+
+ /**
+ * A default JEXL engine instance.
+ */
+ protected final JexlEngine JEXL;
+
+ public JexlTestCase(final String name) {
+ this(name, new JexlBuilder().imports(Arrays.asList("java.lang",
"java.math")).permissions(null).cache(128).create());
+ }
+
+ protected JexlTestCase(final String name, final JexlEngine jexl) {
+ //super(name);
+ JEXL = jexl;
+ }
+
+ static JexlEngine createEngine() {
+ return new JexlBuilder().create();
+ }
+
+ public static JexlEngine createEngine(final boolean lenient) {
+ return createEngine(lenient, TEST_PERMS);
+ }
+
+ public static JexlEngine createEngine(final boolean lenient, final
JexlPermissions permissions) {
+ return new JexlBuilder().uberspect(new Uberspect(null, null,
permissions)).arithmetic(new JexlArithmetic(!lenient)).cache(128).create();
+ }
+
+ static JexlEngine createEngine(final JexlFeatures features) {
+ return new JexlBuilder().features(features).create();
+ }
+
+ /**
+ * Will force testing the debugger for each derived test class by
+ * recreating each expression from the JexlNode in the JexlEngine cache &
+ * testing them for equality with the origin.
+ *
+ * @throws Exception
+ */
+ public static void debuggerCheck(final JexlEngine ijexl) throws Exception {
+ Util.debuggerCheck(ijexl);
+ }
+
+ /**
+ * Compare strings ignoring white space differences.
+ * <p>This replaces any sequence of whitespaces (ie \\s) by one space (ie
ASCII 32) in both
+ * arguments then compares them.</p>
+ *
+ * @param lhs left hand side
+ * @param rhs right hand side
+ * @return true if strings are equal besides whitespace
+ */
+ public static boolean equalsIgnoreWhiteSpace(final String lhs, final String
rhs) {
+ final String lhsw = lhs.trim().replaceAll("\\s+", "");
+ final String rhsw = rhs.trim().replaceAll("\\s+", "");
+ return lhsw.equals(rhsw);
+ }
+
+ public static String toString(final JexlScript script) {
+ final Debugger d = new Debugger().lineFeed("").indentation(0);
+ d.debug(script);
+ return d.toString();
+ }
+
+ @BeforeAll
+ static void setUpClass() {
+ JexlBuilder.setDefaultPermissions(TEST_PERMS);
+ }
+
+ public String simpleWhitespace(final String arg) {
+ return arg.trim().replaceAll("\\s+", " ");
+ }
+
+ public void setUp() throws Exception {
+ // nothing to do
+ }
+
+ @AfterEach
+ public void tearDown() throws Exception {
+ debuggerCheck(JEXL);
+ }
+
+ public static class PragmaticContext extends OptionsContext implements
JexlContext.PragmaProcessor, JexlContext.OptionsHandle {
+ private final JexlOptions options;
+
+ public PragmaticContext() {
+ this(new JexlOptions());
+ }
+
+ public PragmaticContext(final JexlOptions o) {
+ this.options = o;
+ }
+
+ @Override
+ public JexlOptions getEngineOptions() {
+ return options;
+ }
+
+ @Override
+ public void processPragma(final JexlOptions opts, final String key, final
Object value) {
+ if ("script.mode".equals(key) && "pro50".equals(value)) {
+ opts.set(MODE_PRO50);
+ }
+ }
+
+ @Override
+ public void processPragma(final String key, final Object value) {
+ processPragma(null, key, value);
+ }
+ }
}
diff --git a/src/test/java/org/apache/commons/jexl3/SwitchTest.java
b/src/test/java/org/apache/commons/jexl3/SwitchTest.java
index 7d2eb332..83c8eb97 100644
--- a/src/test/java/org/apache/commons/jexl3/SwitchTest.java
+++ b/src/test/java/org/apache/commons/jexl3/SwitchTest.java
@@ -16,7 +16,6 @@
*/
package org.apache.commons.jexl3;
-import static
org.apache.commons.jexl3.introspection.JexlPermissions.RESTRICTED;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -140,7 +139,7 @@ public class SwitchTest extends JexlTestCase {
@Test
void test440p() {
- Assertions.assertTrue(RESTRICTED.allow(this.getClass()));
+ Assertions.assertTrue(TEST_PERMS.allow(this.getClass()));
}
@Test
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 41f65db5..810c941e 100644
--- a/src/test/java/org/apache/commons/jexl3/examples/StreamTest.java
+++ b/src/test/java/org/apache/commons/jexl3/examples/StreamTest.java
@@ -17,10 +17,12 @@
package org.apache.commons.jexl3.examples;
import static java.lang.Boolean.TRUE;
+import static org.apache.commons.jexl3.JexlTestCase.TEST_PERMS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.lang.reflect.Method;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
@@ -38,12 +40,18 @@ 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.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* A test around scripting streams.
*/
-class StreamTest {
+public class StreamTest {
+ @BeforeAll
+ static void setUpClass() {
+ JexlBuilder.setDefaultPermissions(TEST_PERMS);
+ }
/**
* A MapContext that can operate on streams and collections.
@@ -108,25 +116,31 @@ class StreamTest {
/** Our engine instance. */
private final JexlEngine jexl;
+ private final JexlPermissions jexlPermissions;
- public StreamTest() {
+ public StreamTest() throws Exception {
// Restricting features; no loops, no side effects
final JexlFeatures features = new JexlFeatures()
.loops(false)
.sideEffectGlobal(false)
.sideEffect(false);
// Restricted permissions to a safe set but with URI allowed
- final JexlPermissions permissions = new
ClassPermissions(java.net.URI.class);
- // Create the engine
+ jexlPermissions = new ClassPermissions(java.net.URI.class,
StreamContext.class, CollectionContext.class);
+ // Create the engine
jexl = new JexlBuilder()
.features(features)
- .permissions(permissions)
+ .permissions(jexlPermissions)
.namespaces(Collections.singletonMap("URI", java.net.URI.class))
.create();
}
@Test
- void testURICollection() {
+ void testURICollection() throws Exception {
+ Assertions.assertTrue(jexlPermissions.allow(java.net.URI.class));
+
Assertions.assertTrue(jexlPermissions.allow(java.util.stream.Stream.class));
+ for (Method m : Arrays.stream(Stream.class.getMethods()).filter(m ->
m.getName().equals("filter")).collect(Collectors.toSet())) {
+ Assertions.assertTrue(jexlPermissions.allow(m));
+ }
// A collection map/filter aware context
final JexlContext sctxt = new CollectionContext();
// Some uris
@@ -162,7 +176,9 @@ class StreamTest {
}
@Test
- void testURIStream() {
+ void testURIStream() throws Exception {
+
Assertions.assertTrue(jexlPermissions.allow(StreamContext.class.getMethod("filter",
new Class[]{Stream.class, JexlScript.class})));
+
// Assume a collection of uris need to be processed and transformed to
be simplified ;
// we want only http/https ones, only the host part and using a https
scheme
final List<URI> uris = Arrays.asList(
diff --git
a/src/test/java/org/apache/commons/jexl3/internal/introspection/DiscoveryTest.java
b/src/test/java/org/apache/commons/jexl3/internal/introspection/DiscoveryTest.java
index 512c9db7..42ecf1ae 100644
---
a/src/test/java/org/apache/commons/jexl3/internal/introspection/DiscoveryTest.java
+++
b/src/test/java/org/apache/commons/jexl3/internal/introspection/DiscoveryTest.java
@@ -32,11 +32,13 @@ import org.apache.commons.jexl3.internal.Engine;
import org.apache.commons.jexl3.introspection.JexlMethod;
import org.apache.commons.jexl3.introspection.JexlPropertyGet;
import org.apache.commons.jexl3.introspection.JexlPropertySet;
+import org.apache.commons.jexl3.introspection.JexlUberspect;
import org.junit.jupiter.api.Test;
/**
* Tests for checking introspection discovery.
*/
+@SuppressWarnings("unused")
class DiscoveryTest extends JexlTestCase {
public static class Bean {
private String value;
@@ -144,7 +146,7 @@ class DiscoveryTest extends JexlTestCase {
@Test
void testBeanIntrospection() throws Exception {
- final Uberspect uber = Engine.getUberspect(null, null);
+ final JexlUberspect uber = Engine.getUberspect(null, null,
JexlTestCase.TEST_PERMS);
final Bean bean = new Bean("JEXL", "LXEJ");
final JexlPropertyGet get = uber.getPropertyGet(bean, "value");
@@ -173,7 +175,7 @@ class DiscoveryTest extends JexlTestCase {
@Test
void testDuckIntrospection() throws Exception {
- final Uberspect uber = Engine.getUberspect(null, null);
+ final JexlUberspect uber = Engine.getUberspect(null, null,
JexlTestCase.TEST_PERMS);
final Duck duck = new Duck("JEXL", "LXEJ");
final JexlPropertyGet get = uber.getPropertyGet(duck, "value");
@@ -201,7 +203,7 @@ class DiscoveryTest extends JexlTestCase {
@Test
void testListIntrospection() throws Exception {
- final Uberspect uber = Engine.getUberspect(null, null);
+ final JexlUberspect uber = new Uberspect(null, null);
final List<Object> list = new ArrayList<>();
list.add("LIST");
list.add("TSIL");
@@ -215,7 +217,7 @@ class DiscoveryTest extends JexlTestCase {
assertEquals(set, uber.getPropertySet(list, 1, "foo"));
// different property should return different setter/getter
assertNotEquals(get, uber.getPropertyGet(list, 0));
- assertNotEquals(get, uber.getPropertySet(list, 0, "foo"));
+ assertNotEquals(set, uber.getPropertySet(list, 0, "foo"));
// setter returns argument
final Object bar = set.invoke(list, "bar");
assertEquals("bar", bar);
@@ -232,7 +234,7 @@ class DiscoveryTest extends JexlTestCase {
@Test
void testMapIntrospection() throws Exception {
- final Uberspect uber = Engine.getUberspect(null, null);
+ final JexlUberspect uber = new Uberspect(null, null);
final Map<String, Object> map = new HashMap<>();
map.put("value", "MAP");
map.put("eulav", "PAM");
@@ -246,7 +248,7 @@ class DiscoveryTest extends JexlTestCase {
assertEquals(set, uber.getPropertySet(map, "value", "foo"));
// different property should return different setter/getter
assertNotEquals(get, uber.getPropertyGet(map, "eulav"));
- assertNotEquals(get, uber.getPropertySet(map, "eulav", "foo"));
+ assertNotEquals(set, uber.getPropertySet(map, "eulav", "foo"));
// setter returns argument
final Object bar = set.invoke(map, "bar");
assertEquals("bar", bar);
@@ -263,7 +265,7 @@ class DiscoveryTest extends JexlTestCase {
@Test
void testMethodIntrospection() throws Exception {
- final Uberspect uber = new Uberspect(null, null);
+ final JexlUberspect uber = new Uberspect(null, null,
JexlTestCase.TEST_PERMS);
final Bulgroz bulgroz = new Bulgroz();
JexlMethod jmethod;
Object result;
diff --git
a/src/test/java/org/apache/commons/jexl3/internal/introspection/PermissionsTest.java
b/src/test/java/org/apache/commons/jexl3/internal/introspection/PermissionsTest.java
index 9d970470..23c22188 100644
---
a/src/test/java/org/apache/commons/jexl3/internal/introspection/PermissionsTest.java
+++
b/src/test/java/org/apache/commons/jexl3/internal/introspection/PermissionsTest.java
@@ -54,7 +54,7 @@ import org.junit.jupiter.api.Test;
/**
* Checks the CacheMap.MethodKey implementation
*/
-
+@SuppressWarnings({"unused","empty"})
class PermissionsTest {
public static class A {
@@ -464,7 +464,7 @@ class PermissionsTest {
@Test
void testSecurePermissions() {
- assertNotNull(JexlTestCase.SECURE);
+ assertNotNull(JexlTestCase.TEST_PERMS);
final List<Class<?>> acs = Arrays.asList(
java.lang.Runtime.class,
java.math.BigDecimal.class,
@@ -473,7 +473,7 @@ class PermissionsTest {
for(final Class<?> ac: acs) {
final Package p = ac.getPackage();
assertNotNull(p, ac::getName);
- assertTrue(JexlTestCase.SECURE.allow(p), ac::getName);
+ assertTrue(JexlTestCase.TEST_PERMS.allow(p), ac::getName);
}
final List<Class<?>> nacs = Arrays.asList(
java.lang.annotation.ElementType.class,
@@ -483,10 +483,10 @@ class PermissionsTest {
java.lang.ref.SoftReference.class,
java.lang.reflect.Method.class);
for(final Class<?> nac : nacs) {
- assertFalse(JexlTestCase.SECURE.allow(nac), nac::getName);
+ assertFalse(JexlTestCase.TEST_PERMS.allow(nac), nac::getName);
final Package p = nac.getPackage();
assertNotNull(p, nac::getName);
- assertFalse(JexlTestCase.SECURE.allow(p), nac::getName);
+ assertFalse(JexlTestCase.TEST_PERMS.allow(p), nac::getName);
}
}
@@ -508,10 +508,11 @@ class PermissionsTest {
}
@Test
- void testPair0() {
+ void testPair0() throws Exception {
final Map<String, Object> funcs = new HashMap<>();
funcs.put("lisp", new Scheme());
- final JexlPermissions permissions =
JexlPermissions.RESTRICTED.compose("org.example.*");
+ final JexlPermissions permissions =
JexlPermissions.RESTRICTED.compose("org.example.*",
"org.apache.commons.jexl3.internal.introspection { +PermissionsTest$Scheme {}");
+ Assertions.assertTrue(permissions.allow(Scheme.class.getMethod("cons",
new Class[]{ Object.class, Object.class})));
final JexlEngine jexl = new
JexlBuilder().cache(8).permissions(permissions).namespaces(funcs).create();
String src = "let p = lisp:cons(17, 25); p.car + p.cdr;";
JexlScript script = jexl.createScript(src);
diff --git
a/src/test/java/org/apache/commons/jexl3/scripting/JexlScriptEngineOptionalTest.java
b/src/test/java/org/apache/commons/jexl3/scripting/JexlScriptEngineOptionalTest.java
index 1206dfa6..92d1c5ad 100644
---
a/src/test/java/org/apache/commons/jexl3/scripting/JexlScriptEngineOptionalTest.java
+++
b/src/test/java/org/apache/commons/jexl3/scripting/JexlScriptEngineOptionalTest.java
@@ -27,6 +27,9 @@ import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
+import org.apache.commons.jexl3.JexlBuilder;
+import org.apache.commons.jexl3.JexlTestCase;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
class JexlScriptEngineOptionalTest {
@@ -34,6 +37,11 @@ class JexlScriptEngineOptionalTest {
private final ScriptEngineManager manager = new ScriptEngineManager();
private final ScriptEngine engine = manager.getEngineByName("jexl");
+ @BeforeAll
+ static void setUpClass() {
+ JexlBuilder.setDefaultPermissions(JexlTestCase.TEST_PERMS);
+ }
+
@Test
void testCompilable() throws Exception {
assertInstanceOf(Compilable.class, engine, "Engine should implement
Compilable");
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 bd1b622e..626bb5e1 100644
--- a/src/test/java/org/apache/commons/jexl3/scripting/JexlScriptEngineTest.java
+++ b/src/test/java/org/apache/commons/jexl3/scripting/JexlScriptEngineTest.java
@@ -140,6 +140,8 @@ class JexlScriptEngineTest {
@Test
void testErrors() throws Exception {
+ JexlPermissions permissions = new
JexlPermissions.ClassPermissions(Errors.class);
+ JexlScriptEngine.setPermissions(permissions);
final ScriptEngineManager manager = new ScriptEngineManager();
final JexlScriptEngine engine = (JexlScriptEngine)
manager.getEngineByName("JEXL");
engine.put("errors", new Errors());
@@ -259,8 +261,7 @@ class JexlScriptEngineTest {
@Test
void testScriptingPermissions1() throws Exception {
- JexlBuilder.setDefaultPermissions(JexlPermissions.UNRESTRICTED);
- JexlScriptEngine.setPermissions(null);
+ JexlScriptEngine.setPermissions(JexlPermissions.UNRESTRICTED);
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("jexl3");
final Long time2 = (Long) engine.eval(
@@ -292,24 +293,28 @@ class JexlScriptEngineTest {
@Test
void testMain2() throws Exception {
- final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
final PrintStream originalOut = System.out;
Path file = null;
try {
- System.setOut(new PrintStream(outContent));
file = Files.createTempFile("test-jsr233", ".jexl");
- final BufferedWriter writer = new BufferedWriter(new
FileWriter(file.toFile()));
- writer.write("a=20;\nb=22;\na+b\n");
- writer.close();
- final String ctl = ">>: 42" + LF;
- Main.main(new String[]{file.toString()});
- Assertions.assertEquals(ctl, outContent.toString());
+ try (BufferedWriter writer = new BufferedWriter(new
FileWriter(file.toFile()));
+ ByteArrayOutputStream outContent = new
ByteArrayOutputStream();
+ PrintStream printStream = new PrintStream(outContent)) {
+
+ writer.write("a=20;\nb=22;\na+b\n");
+ writer.close(); // Explicit close before using the file
+
+ System.setOut(printStream);
+ final String ctl = ">>: 42" + LF;
+ Main.main(new String[]{file.toString()});
+ Assertions.assertEquals(ctl, outContent.toString());
+ } finally {
+ System.setOut(originalOut);
+ }
} finally {
- System.setOut(originalOut);
if (file != null) {
Files.delete(file);
}
}
}
-
}