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 2022/02/03 16:55:39 UTC
[commons-jexl] branch master updated: JEXL-357: added permissions parser; - added a (static) 'secure' permissions (used in tests); - quiesce spotbugs on builder patterns;
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 0b1cbdb JEXL-357: added permissions parser; - added a (static) 'secure' permissions (used in tests); - quiesce spotbugs on builder patterns;
new 6ad1244 Merge remote-tracking branch 'origin/master'
0b1cbdb is described below
commit 0b1cbdbe8aa7d98d71dbfe29279240adde8c136f
Author: henrib <he...@apache.org>
AuthorDate: Thu Feb 3 17:55:23 2022 +0100
JEXL-357: added permissions parser;
- added a (static) 'secure' permissions (used in tests);
- quiesce spotbugs on builder patterns;
---
src/main/config/findbugs-exclude-filter.xml | 16 +
.../jexl3/internal/introspection/ClassMap.java | 53 ++-
.../jexl3/internal/introspection/Introspector.java | 13 +-
.../jexl3/internal/introspection/Permissions.java | 413 ++++++++++++++-------
...ermissionParser.java => PermissionsParser.java} | 102 +++--
.../jexl3/internal/introspection/Uberspect.java | 5 +-
.../jexl3/introspection/JexlPermissions.java | 65 ++++
.../org/apache/commons/jexl3/JexlTestCase.java | 6 +-
.../apache/commons/jexl3/PropertyAccessTest.java | 10 +-
.../introspection/MiscIntrospectionTest.java | 127 +------
...{MiscIntrospectionTest.java => NoJexlTest.java} | 97 ++---
...IntrospectionTest.java => PermissionsTest.java} | 148 ++++----
.../apache/commons/jexl3/jexl342/OptionalTest.java | 4 +
13 files changed, 595 insertions(+), 464 deletions(-)
diff --git a/src/main/config/findbugs-exclude-filter.xml b/src/main/config/findbugs-exclude-filter.xml
index 2f70690..18f97c3 100644
--- a/src/main/config/findbugs-exclude-filter.xml
+++ b/src/main/config/findbugs-exclude-filter.xml
@@ -51,4 +51,20 @@
<Match>
<Class name="org.apache.commons.jexl3.parser.SimpleNode"/>
</Match>
+ <Match>
+ <Class name="org.apache.commons.jexl3.JexlBuilder"/>
+ <Bug code="EI2,EI"></Bug>
+ </Match>
+ <Match>
+ <Package name="org.apache.commons.jexl3.internal"/>
+ <Bug code="EI2,EI"></Bug>
+ </Match>
+ <Match>
+ <Package name="org.apache.commons.jexl3.introspection.internal"/>
+ <Bug code="EI2,EI"></Bug>
+ </Match>
+ <Match>
+ <Package name="org.apache.commons.jexl3.parser"/>
+ <Bug code="EI2,EI"></Bug>
+ </Match>
</FindBugsFilter>
diff --git a/src/main/java/org/apache/commons/jexl3/internal/introspection/ClassMap.java b/src/main/java/org/apache/commons/jexl3/internal/introspection/ClassMap.java
index 5f435b8..770342b 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/introspection/ClassMap.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/introspection/ClassMap.java
@@ -16,21 +16,21 @@
*/
package org.apache.commons.jexl3.internal.introspection;
+import org.apache.commons.jexl3.introspection.JexlPermissions;
import org.apache.commons.logging.Log;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-
+import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
/**
* A cache of introspection information for a specific class instance.
@@ -77,17 +77,50 @@ final class ClassMap {
* </p>
* Uses ConcurrentMap since 3.0, marginally faster than 2.1 under contention.
*/
- private final ConcurrentMap<MethodKey, Method> byKey = new ConcurrentHashMap<>();
+ private final Map<MethodKey, Method> byKey ;
/**
* Keep track of all methods with the same name; this is not modified after creation.
*/
- private final Map<String, Method[]> byName = new HashMap<>();
+ private final Map<String, Method[]> byName;
/**
* Cache of fields.
*/
private final Map<String, Field> fieldCache;
/**
+ * Singleton for permissions non-allowed classes.
+ */
+ private static final ClassMap EMPTY = new ClassMap();
+
+ /**
+ * @return the empty classmap instance
+ */
+ static ClassMap empty() {
+ return EMPTY;
+ }
+
+ /**
+ * Empty map.
+ */
+ private ClassMap() {
+ this.byKey = Collections.unmodifiableMap(new AbstractMap<MethodKey, Method>() {
+ @Override
+ public String toString() {
+ return "emptyClassMap{}";
+ }
+ @Override
+ public Set<Entry<MethodKey, Method>> entrySet() {
+ return null;
+ }
+ @Override public Method get(Object name) {
+ return CACHE_MISS;
+ }
+ });
+ this.byName = Collections.emptyMap();
+ this.fieldCache = Collections.emptyMap();
+ }
+
+ /**
* Standard constructor.
*
* @param aClass the class to deconstruct.
@@ -95,7 +128,9 @@ final class ClassMap {
* @param log the logger.
*/
@SuppressWarnings("LeakingThisInConstructor")
- ClassMap(final Class<?> aClass, final Permissions permissions, final Log log) {
+ ClassMap(final Class<?> aClass, final JexlPermissions permissions, final Log log) {
+ this.byKey = new ConcurrentHashMap<>();
+ this.byName = new HashMap<>();
// eagerly cache methods
create(this, permissions, aClass, log);
// eagerly cache public fields
@@ -211,7 +246,7 @@ final class ClassMap {
* @param clazz the class to cache
* @param log the Log
*/
- private static void create(final ClassMap cache, final Permissions permissions, Class<?> clazz, final Log log) {
+ private static void create(final ClassMap cache, final JexlPermissions permissions, Class<?> clazz, final Log log) {
//
// Build a list of all elements in the class hierarchy. This one is bottom-first (i.e. we start
// with the actual declaring class and its interfaces and then move up (superclass etc.) until we
@@ -263,7 +298,7 @@ final class ClassMap {
* @param log the Log
*/
private static void populateWithInterface(final ClassMap cache,
- final Permissions permissions,
+ final JexlPermissions permissions,
final Class<?> iface,
final Log log) {
if (Modifier.isPublic(iface.getModifiers())) {
@@ -284,7 +319,7 @@ final class ClassMap {
* @param log the Log
*/
private static void populateWithClass(final ClassMap cache,
- final Permissions permissions,
+ final JexlPermissions permissions,
final Class<?> clazz,
final Log log) {
try {
diff --git a/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java b/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java
index 8054ba5..ee534da 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java
@@ -16,6 +16,7 @@
*/
package org.apache.commons.jexl3.internal.introspection;
+import org.apache.commons.jexl3.introspection.JexlPermissions;
import org.apache.commons.logging.Log;
import java.lang.reflect.Constructor;
@@ -69,7 +70,7 @@ public final class Introspector {
/**
* The permissions.
*/
- private final Permissions permissions;
+ private final JexlPermissions permissions;
/**
* The read/write lock.
*/
@@ -93,7 +94,7 @@ public final class Introspector {
* @param cloader the class loader
*/
public Introspector(final Log log, final ClassLoader cloader) {
- this(log, cloader, null);
+ this(log, cloader, Permissions.DEFAULT);
}
/**
@@ -102,10 +103,10 @@ public final class Introspector {
* @param cloader the class loader
* @param perms the permissions
*/
- public Introspector(final Log log, final ClassLoader cloader, final Permissions perms) {
+ public Introspector(final Log log, final ClassLoader cloader, final JexlPermissions perms) {
this.logger = log;
this.loader = cloader;
- this.permissions = perms != null? perms : Permissions.DEFAULT;
+ this.permissions = perms != null? perms : Permissions.SECURE;
}
/**
@@ -312,7 +313,9 @@ public final class Introspector {
// try again
classMap = classMethodMaps.get(c);
if (classMap == null) {
- classMap = new ClassMap(c, permissions, logger);
+ classMap = permissions.allow(c)
+ ? new ClassMap(c, permissions, logger)
+ : ClassMap.empty();
classMethodMaps.put(c, classMap);
}
} finally {
diff --git a/src/main/java/org/apache/commons/jexl3/internal/introspection/Permissions.java b/src/main/java/org/apache/commons/jexl3/internal/introspection/Permissions.java
index 7f1ffae..3df5385 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/introspection/Permissions.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/introspection/Permissions.java
@@ -29,20 +29,25 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.jexl3.annotations.NoJexl;
+import org.apache.commons.jexl3.introspection.JexlPermissions;
/**
* Checks whether an element (ctor, field or method) is visible by JEXL introspection.
* Default implementation does this by checking if element has been annotated with NoJexl.
+ *
+ * The NoJexl annotation allows a fine grain permissions on executable objects (methods, fields, constructors).
+ * NoJexl of a package implies all classes (including derived classes) and all interfaces
+ * of that package are invisible to Jexl;
+ * NoJexl on a class implies this class and all its derived classes are invisible to Jexl;
+ * NoJexl on a (public) field makes it not visible as a property to Jexl;
+ * NoJexl on a constructor prevents that constructor to be used to instantiate through 'new';
+ * NoJexl on a method prevents that method and any of its overrides to be visible to Jexl;
+ * NoJexl on an interface prevents all methods of that interface and their overrides to be visible to Jexl.
+ *
+ * It is possible to further refine permissions on classes used through libraries where source code form can
+ * not be altered. The @link(PermissionsParser).
*/
-public class Permissions {
- /** Allow inheritance. */
- protected Permissions() {
- packages = Collections.emptyMap();
- }
- /**
- * The default singleton.
- */
- public static final Permissions DEFAULT = new Permissions();
+public class Permissions implements JexlPermissions {
/**
* Equivalent of @NoJexl on a class in a package.
@@ -72,7 +77,46 @@ public class Permissions {
}
NoJexlClass getNoJexl(Class<?> clazz) {
- return nojexl.get(clazz.getName());
+ return nojexl.get(classKey(clazz));
+ }
+
+ void addNoJexl(String key, NoJexlClass njc) {
+ nojexl.put(key, njc);
+ }
+ }
+
+ /**
+ * Creates a class key joining enclosing ascendants with '$'.
+ * <p>As in <code>outer$inner</code> for <code>class outer { class inner...</code>.</p>
+ * @param clazz the clazz
+ * @return the clazz key
+ */
+ private static String classKey(final Class<?> clazz) {
+ return classKey(clazz, null);
+ }
+
+ /**
+ * Creates a class key joining enclosing ascendants with '$'.
+ * <p>As in <code>outer$inner</code> for <code>class outer { class inner...</code>.</p>
+ * @param clazz the clazz
+ * @param strb the buffer to compose the key
+ * @return the clazz key
+ */
+ private static String classKey(final Class<?> clazz, final StringBuilder strb) {
+ StringBuilder keyb = strb;
+ Class<?> outer = clazz.getEnclosingClass();
+ if (outer != null) {
+ if (keyb == null) {
+ keyb = new StringBuilder();
+ }
+ classKey(outer, keyb);
+ keyb.append('$');
+ }
+ if (keyb != null) {
+ keyb.append(clazz.getSimpleName());
+ return keyb.toString();
+ } else {
+ return clazz.getSimpleName();
}
}
@@ -90,6 +134,8 @@ public class Permissions {
fieldNames = fields;
}
+ boolean isEmpty() { return methodNames.isEmpty() && fieldNames.isEmpty(); }
+
NoJexlClass() {
this(new HashSet<>(), new HashSet<>());
}
@@ -102,8 +148,8 @@ public class Permissions {
return methodNames.contains(method.getName());
}
- boolean deny(Constructor method) {
- return methodNames.contains(method.getName());
+ boolean deny(Constructor<?> method) {
+ return methodNames.contains(method.getDeclaringClass().getSimpleName());
}
}
@@ -113,12 +159,14 @@ public class Permissions {
boolean deny(Field field) {
return true;
}
+
@Override
boolean deny(Method method) {
return true;
}
+
@Override
- boolean deny(Constructor method) {
+ boolean deny(Constructor<?> method) {
return true;
}
};
@@ -129,12 +177,14 @@ public class Permissions {
boolean deny(Field field) {
return false;
}
+
@Override
boolean deny(Method method) {
return false;
}
+
@Override
- boolean deny(Constructor method) {
+ boolean deny(Constructor<?> method) {
return false;
}
};
@@ -157,6 +207,69 @@ public class Permissions {
* The @NoJexl execution-time map.
*/
private final Map<String, NoJexlPackage> packages;
+ /**
+ * The closed world package patterns.
+ */
+ private final Set<String> allowed;
+
+ /** Allow inheritance. */
+ protected Permissions() {
+ this(Collections.emptySet(), Collections.emptyMap());
+ }
+
+ /**
+ * Default ctor.
+ * @param perimeter the allowed wildcard set of packages
+ * @param nojexl the NoJexl external map
+ */
+ protected Permissions(Set<String> perimeter, Map<String, NoJexlPackage> nojexl) {
+ this.allowed = perimeter;
+ this.packages = nojexl;
+ }
+
+ /**
+ * The default singleton.
+ */
+ public static final Permissions DEFAULT = new Permissions();
+
+ /**
+ * A very secure singleton.
+ */
+ public static final Permissions SECURE = new PermissionsParser().parse(
+ "# Secure Uberspect Permissions",
+ "java.nio.*",
+ "java.io.*",
+ "java.lang.*",
+ "java.math.*",
+ "java.text.*",
+ "java.util.*",
+ "org.w3c.dom.*",
+ "org.apache.commons.jexl3.*",
+ "java.lang { Runtime {} System {} ProcessBuilder {} Class {} }",
+ "java.lang.annotation {}",
+ "java.lang.instrument {}",
+ "java.lang.invoke {}",
+ "java.lang.management {}",
+ "java.lang.ref {}",
+ "java.lang.reflect {}",
+ "java.net {}",
+ "java.io { File { } }",
+ "java.nio { Path { } Paths { } Files { } }"
+ );
+
+ /**
+ * @return the packages
+ */
+ Map<String, NoJexlPackage> getPackages() {
+ return packages == null? Collections.emptyMap() : Collections.unmodifiableMap(packages);
+ }
+
+ /**
+ * @return the wilcards
+ */
+ Set<String> getWildcards() {
+ return allowed == null? Collections.emptySet() : Collections.unmodifiableSet(allowed);
+ }
/**
* Gets the package constraints.
@@ -165,17 +278,55 @@ public class Permissions {
*/
private NoJexlPackage getNoJexl(Package pack) {
NoJexlPackage njp = packages.get(pack.getName());
- return njp == null? JEXL_PACKAGE : njp;
+ if (njp != null) {
+ return njp;
+ }
+ return JEXL_PACKAGE;
}
/**
* Gets the class constraints.
+ * <p>If nothing was explicitly forbidden, everything is allowed.</p>
* @param clazz the class
* @return the class constraints instance, not-null.
*/
private NoJexlClass getNoJexl(Class<?> clazz) {
NoJexlPackage njp = getNoJexl(clazz.getPackage());
- return njp == null? JEXL_CLASS : njp.getNoJexl(clazz);
+ if (njp != null) {
+ NoJexlClass njc = njp.getNoJexl(clazz);
+ if (njc != null) {
+ return njc;
+ }
+ }
+ return JEXL_CLASS;
+ }
+
+ /**
+ * Whether the wilcard set of packages allows a given class to be introspected.
+ * @param clazz the package name (not null)
+ * @return true if allowed, false otherwise
+ */
+ private boolean wildcardAllow(Class<?> clazz) {
+ return wildcardAllow(allowed, clazz.getPackage().getName());
+ }
+
+ /**
+ * Whether the wilcard set of packages allows a given package to be introspected.
+ * @param allowed the allowed set (not null, may be empty)
+ * @param name the package name (not null)
+ * @return true if allowed, false otherwise
+ */
+ static boolean wildcardAllow(Set<String> allowed, String name) {
+ // allowed packages are explicit in this case
+ boolean found = allowed == null || allowed.isEmpty() || allowed.contains(name);
+ if (!found) {
+ String wildcard = name;
+ for (int i = name.length(); !found && i > 0; i = wildcard.lastIndexOf('.')) {
+ wildcard = wildcard.substring(0, i);
+ found = allowed.contains(wildcard + ".*");
+ }
+ }
+ return found;
}
/**
@@ -184,17 +335,33 @@ public class Permissions {
* @return true if denied, false otherwise
*/
private boolean deny(Package pack) {
+ // is package annotated with nojexl ?
+ final NoJexl nojexl = pack.getAnnotation(NoJexl.class);
+ if (nojexl != null) {
+ return true;
+ }
return Objects.equals(NOJEXL_PACKAGE, packages.get(pack.getName()));
}
/**
* Whether a whole class is denied Jexl visibility.
+ * <p>Also checks package visibility.</p>
* @param clazz the class
* @return true if denied, false otherwise
*/
private boolean deny(Class<?> clazz) {
- NoJexlPackage njp = packages.get(clazz.getPackage().getName());
- return njp != null && NOJEXL_CLASS.equals(njp.getNoJexl(clazz));
+ // dont deny arrays
+ if (clazz.isArray()) {
+ return false;
+ }
+ // is clazz annotated with nojexl ?
+ final NoJexl nojexl = clazz.getAnnotation(NoJexl.class);
+ if (nojexl != null) {
+ return true;
+ }
+ Package pkg = clazz.getPackage();
+ NoJexlPackage njp = packages.get(pkg.getName());
+ return njp != null && Objects.equals(NOJEXL_CLASS, njp.getNoJexl(clazz));
}
/**
@@ -203,6 +370,15 @@ public class Permissions {
* @return true if denied, false otherwise
*/
private boolean deny(Constructor<?> ctor) {
+ // only public
+ if (!Modifier.isPublic(ctor.getModifiers())) {
+ return true;
+ }
+ // is ctor annotated with nojexl ?
+ final NoJexl nojexl = ctor.getAnnotation(NoJexl.class);
+ if (nojexl != null) {
+ return true;
+ }
return getNoJexl(ctor.getDeclaringClass()).deny(ctor);
}
@@ -212,6 +388,15 @@ public class Permissions {
* @return true if denied, false otherwise
*/
private boolean deny(Field field) {
+ // only public
+ if (!Modifier.isPublic(field.getModifiers())) {
+ return true;
+ }
+ // is field annotated with nojexl ?
+ final NoJexl nojexl = field.getAnnotation(NoJexl.class);
+ if (nojexl != null) {
+ return true;
+ }
return getNoJexl(field.getDeclaringClass()).deny(field);
}
@@ -221,6 +406,15 @@ public class Permissions {
* @return true if denied, false otherwise
*/
private boolean deny(Method method) {
+ // only public
+ if (!Modifier.isPublic(method.getModifiers())) {
+ return true;
+ }
+ // is method annotated with nojexl ?
+ final NoJexl nojexl = method.getAnnotation(NoJexl.class);
+ if (nojexl != null) {
+ return true;
+ }
return getNoJexl(method.getDeclaringClass()).deny(method);
}
@@ -229,11 +423,9 @@ public class Permissions {
* @param pack the package
* @return true if JEXL is allowed to introspect, false otherwise
*/
+ @Override
public boolean allow(final Package pack) {
- if (pack == null || pack.getAnnotation(NoJexl.class) != null || deny(pack)) {
- return false;
- }
- return true;
+ return pack != null && !deny(pack);
}
/**
@@ -242,8 +434,24 @@ public class Permissions {
* @param clazz the class to check
* @return true if JEXL is allowed to introspect, false otherwise
*/
+ @Override
public boolean allow(final Class<?> clazz) {
- return clazz != null && allow(clazz.getPackage()) && allow(clazz, true);
+ if (clazz == null) {
+ return false;
+ }
+ // class must be allowed
+ if (deny(clazz)) {
+ return false;
+ }
+ // all super classes must be allowed
+ Class<?> walk = clazz.getSuperclass();
+ while (walk != null) {
+ if (deny(walk)) {
+ return false;
+ }
+ walk = walk.getSuperclass();
+ }
+ return true;
}
/**
@@ -251,27 +459,22 @@ public class Permissions {
* @param ctor the constructor to check
* @return true if JEXL is allowed to introspect, false otherwise
*/
+ @Override
public boolean allow(final Constructor<?> ctor) {
if (ctor == null) {
return false;
}
- if (!Modifier.isPublic(ctor.getModifiers())) {
+ // check declared restrictions
+ if (deny(ctor)) {
return false;
}
+ // class must agree
final Class<?> clazz = ctor.getDeclaringClass();
- if (!allow(clazz, false)) {
- return false;
- }
- // is ctor annotated with nojexl ?
- final NoJexl nojexl = ctor.getAnnotation(NoJexl.class);
- if (nojexl != null) {
+ if (deny(clazz)) {
return false;
}
- // check added restrictions
- if (deny(ctor)) {
- return false;
- }
- return true;
+ // check wildcards
+ return wildcardAllow(clazz);
}
/**
@@ -279,27 +482,22 @@ public class Permissions {
* @param field the field to check
* @return true if JEXL is allowed to introspect, false otherwise
*/
+ @Override
public boolean allow(final Field field) {
if (field == null) {
return false;
}
- if (!Modifier.isPublic(field.getModifiers())) {
+ // check declared restrictions
+ if (deny(field)) {
return false;
}
+ // class must agree
final Class<?> clazz = field.getDeclaringClass();
- if (!allow(clazz, false)) {
- return false;
- }
- // is field annotated with nojexl ?
- final NoJexl nojexl = field.getAnnotation(NoJexl.class);
- if (nojexl != null) {
+ if (deny(clazz)) {
return false;
}
- // check added restrictions
- if (deny(field)) {
- return false;
- }
- return true;
+ // check wildcards
+ return wildcardAllow(clazz);
}
/**
@@ -309,31 +507,21 @@ public class Permissions {
* @param method the method to check
* @return true if JEXL is allowed to introspect, false otherwise
*/
+ @Override
public boolean allow(final Method method) {
if (method == null) {
return false;
}
- if (!Modifier.isPublic(method.getModifiers())) {
+ // method must be allowed
+ if (!allowMethod(method)) {
return false;
}
- // is method annotated with nojexl ?
- NoJexl nojexl = method.getAnnotation(NoJexl.class);
- if (nojexl != null) {
- return false;
- }
- // is the class annotated with nojexl ?
Class<?> clazz = method.getDeclaringClass();
- nojexl = clazz.getAnnotation(NoJexl.class);
- if (nojexl != null) {
- return false;
- }
- // check added restrictions
- if (deny(method)) {
- return false;
- }
+ // gather if any implementation of the method is explicitly allowed by the packages
+ boolean[] explicit = new boolean[]{wildcardAllow(clazz)};
// lets walk all interfaces
for (final Class<?> inter : clazz.getInterfaces()) {
- if (!allow(inter, method)) {
+ if (!allow(inter, method, explicit)) {
return false;
}
}
@@ -341,95 +529,58 @@ public class Permissions {
clazz = clazz.getSuperclass();
// walk all superclasses
while (clazz != null) {
- if (!allow(clazz, method)) {
+ if (!allow(clazz, method, explicit)) {
return false;
}
clazz = clazz.getSuperclass();
}
- return true;
+ return explicit[0];
}
/**
- * Checks whether a class or one of its superclasses or implemented interfaces
- * explicitly disallows JEXL introspection.
- * @param clazz the class to check
- * @param interf whether interfaces should be checked as well
- * @return true if JEXL is allowed to introspect, false otherwise
+ * Checks whether a method is allowed.
+ * @param method the method
+ * @return true if it has not been disallowed through annotation or declaration
*/
- protected boolean allow(final Class<?> clazz, final boolean interf) {
- if (clazz == null) {
+ private boolean allowMethod(final Method method) {
+ // check declared restrictions
+ if (deny(method)) {
return false;
}
- if (!Modifier.isPublic(clazz.getModifiers())) {
+ Class<?> clazz = method.getDeclaringClass();
+ // class must be allowed
+ if (deny(clazz)) {
return false;
}
- // lets walk all interfaces
- if (interf) {
- for (final Class<?> inter : clazz.getInterfaces()) {
- // is clazz annotated with nojexl ?
- final NoJexl nojexl = inter.getAnnotation(NoJexl.class);
- if (nojexl != null) {
- return false;
- }
- // check added restrictions
- if (deny(inter)) {
- return false;
- }
- }
- }
- // lets walk all super classes
- Class<?> walk = clazz.getSuperclass();
- // walk all superclasses
- while (walk != null) {
- // is clazz annotated with nojexl ?
- final NoJexl nojexl = walk.getAnnotation(NoJexl.class);
- if (nojexl != null) {
- return false;
- }
- // check added restrictions
- if (deny(walk)) {
- return false;
- }
- walk = walk.getSuperclass();
- }
return true;
}
/**
- * Check whether a method is allowed to be JEXL introspected in all its
- * superclasses and interfaces.
- * @param clazz the class
+ * Check whether a method is allowed to be introspected in one superclass or interface.
+ * @param clazz the superclass or interface to check
* @param method the method
+ * @param explicit carries whether the package holding the method is explicitly allowed
* @return true if JEXL is allowed to introspect, false otherwise
*/
- protected boolean allow(final Class<?> clazz, final Method method) {
- if (clazz != null) {
- try {
- // check if method in that class is different from the method argument
- final Method wmethod = clazz.getMethod(method.getName(), method.getParameterTypes());
- if (wmethod != null) {
- NoJexl nojexl = clazz.getAnnotation(NoJexl.class);
- if (nojexl != null) {
- return false;
- }
- // check if parent declaring class said nojexl on that method (transitivity)
- nojexl = wmethod.getAnnotation(NoJexl.class);
- if (nojexl != null) {
- return false;
- }
- // check added restrictions
- if (deny(wmethod)) {
- return false;
- }
- }
- } catch (final NoSuchMethodException ex) {
- // unexpected, return no
- return true;
- } catch (final SecurityException ex) {
- // unexpected, can't do much
+ private boolean allow(final Class<?> clazz, final Method method, boolean[] explicit) {
+ try {
+ // check if method in that class is declared ie overrides
+ final Method override = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
+ // should not be possible...
+ if (!allowMethod(override)) {
return false;
}
+ // explicit |= ...
+ if (!explicit[0]) {
+ explicit[0] = wildcardAllow(clazz);
+ }
+ return true;
+ } catch (final NoSuchMethodException ex) {
+ // will happen if not overriding method in clazz
+ return true;
+ } catch (final SecurityException ex) {
+ // unexpected, can't do much
+ return false;
}
- return true;
}
}
diff --git a/src/main/java/org/apache/commons/jexl3/internal/introspection/PermissionParser.java b/src/main/java/org/apache/commons/jexl3/internal/introspection/PermissionsParser.java
similarity index 75%
rename from src/main/java/org/apache/commons/jexl3/internal/introspection/PermissionParser.java
rename to src/main/java/org/apache/commons/jexl3/internal/introspection/PermissionsParser.java
index 25e6f29..156c7bf 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/introspection/PermissionParser.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/introspection/PermissionsParser.java
@@ -17,13 +17,20 @@
package org.apache.commons.jexl3.internal.introspection;
+import java.util.LinkedHashSet;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
- * Parses permissions akin to NoJexl annotations.
- * The syntax only recognizes packages, classes (and inner classes), methods and fields.
- *
+ * A crude parser to configure permissions akin to NoJexl annotations.
+ * The syntax recognizes 2 types of permissions:
+ * - restricting access to packages, classes (and inner classes), methods and fields
+ * - allowing access to a wildcard restricted set of packages
+ * Example:
+ * my.allowed.packages.*
+ * another.allowed.package.*
+ * # nojexl like restrictions
* my.package {
* class0 {...
* class1 {...}
@@ -32,44 +39,52 @@ import java.util.concurrent.ConcurrentHashMap;
* class3 {}
* }
* # and eol comment
- * class0(); // constructors
- * method(); // method
- * field; // field
- * } // end class0
- * } // end package my.package
+ * class0(); # constructors
+ * method(); # method
+ * field; # field
+ * } # end class0
+ * } # end package my.package
*
*/
-public class PermissionParser {
+public class PermissionsParser {
/** The source. */
private String src;
/** The source size. */
private int size;
/** The @NoJexl execution-time map. */
private Map<String, Permissions.NoJexlPackage> packages;
+ /** The set of wildcard imports. */
+ private Set<String> wildcards;
/**
* Basic ctor.
*/
- PermissionParser() {
+ public PermissionsParser() {
}
- void clear() {
- src = null; size = 0; packages = null;
+ /**
+ * Clears this parser internals.
+ */
+ public void clear() {
+ src = null; size = 0; packages = null; wildcards = null;
}
/**
* Parses permissions from a source.
- * @param src the source
+ * @param srcs the sources
* @return the permissions map
*/
- Map<String, Permissions.NoJexlPackage> parse(String src) {
+ public Permissions parse(String... srcs) {
packages = new ConcurrentHashMap<>();
- this.src = src;
- this.size = src.length();
- readPackages();
- Map<String, Permissions.NoJexlPackage> map = packages;
+ wildcards = new LinkedHashSet<>();
+ for(String src : srcs) {
+ this.src = src;
+ this.size = src.length();
+ readPackages();
+ }
+ Permissions permissions = new Permissions(wildcards, packages);
clear();
- return map;
+ return permissions;
}
/**
@@ -96,12 +111,11 @@ public class PermissionParser {
}
i += 1;
}
- return offset;
+ return i;
}
/**
* Reads spaces.
- * @param offset
* @param offset initial position
* @return position after spaces
*/
@@ -109,7 +123,7 @@ public class PermissionParser {
int i = offset;
while (i < size) {
char c = src.charAt(i);
- if (!Character.isSpaceChar(c)) {
+ if (!Character.isWhitespace(c)) {
break;
}
i += 1;
@@ -121,10 +135,21 @@ public class PermissionParser {
* Reads an identifier (optionally dot-separated).
* @param id the builder to fill the identifier character with
* @param offset the initial reading position
+ * @return the position after the identifier
+ */
+ private int readIdentifier(StringBuilder id, int offset) {
+ return readIdentifier(id, offset, false, false);
+ }
+
+ /**
+ * Reads an identifier (optionally dot-separated).
+ * @param id the builder to fill the identifier character with
+ * @param offset the initial reading position
* @param dot whether dots (.) are allowed
+ * @param star whether stars (*) are allowed
* @return the position after the identifier
*/
- private int readIdentifier(StringBuilder id, int offset, boolean dot) {
+ private int readIdentifier(StringBuilder id, int offset, boolean dot, boolean star) {
int begin = -1;
int i = offset;
char c = 0;
@@ -140,8 +165,10 @@ public class PermissionParser {
if (src.charAt(i - 1) == '.') {
throw new IllegalStateException(unexpected(c, i));
}
- id.append(c);
+ id.append('.');
begin = -1;
+ } else if (star && c == '*') {
+ id.append('*');
} else {
break;
}
@@ -163,7 +190,7 @@ public class PermissionParser {
int i = 0;
int j = -1;
String pname = null;
- for (; i < size; ) {
+ while (i < size) {
char c = src.charAt(i);
// if no parsing progress can be made, we are in error
if (j < i) {
@@ -172,7 +199,7 @@ public class PermissionParser {
throw new IllegalStateException(unexpected(c, i));
}
// get rid of space
- if (Character.isSpaceChar(c)) {
+ if (Character.isWhitespace(c)) {
i = readSpaces(i + 1);
continue;
}
@@ -183,11 +210,16 @@ public class PermissionParser {
}
// read the package qualified name
if (pname == null) {
- int next = readIdentifier(temp, i, true);
+ int next = readIdentifier(temp, i, true, true);
if (i != next) {
pname = temp.toString();
temp.setLength(0);
i = next;
+ // consume it if it is a wildcard decl
+ if (pname.endsWith(".*")) {
+ wildcards.add(pname);
+ pname = null;
+ }
continue;
}
}
@@ -235,7 +267,7 @@ public class PermissionParser {
throw new IllegalStateException(unexpected(c, i));
}
// get rid of space
- if (Character.isSpaceChar(c)) {
+ if (Character.isWhitespace(c)) {
i = readSpaces(i + 1);
continue;
}
@@ -246,7 +278,7 @@ public class PermissionParser {
}
// read an identifier, the class name
if (identifier == null) {
- int next = readIdentifier(temp, i, false);
+ int next = readIdentifier(temp, i);
if (i != next) {
identifier = temp.toString();
temp.setLength(0);
@@ -254,6 +286,10 @@ public class PermissionParser {
continue;
} else if (c == '}') {
i += 1;
+ // restrict the whole class
+ if (njname != null && njclass.isEmpty()) {
+ njpackage.addNoJexl(njname, Permissions.NOJEXL_CLASS);
+ }
break;
}
}
@@ -264,7 +300,7 @@ public class PermissionParser {
// if we have a class, it has a name
njclass = new Permissions.NoJexlClass();
njname = outer != null ? outer + "$" + identifier : identifier;
- njpackage.nojexl.put(njname, njclass);
+ njpackage.addNoJexl(njname, njclass);
identifier = null;
i += 1;
} else {
@@ -275,6 +311,7 @@ public class PermissionParser {
if (c == '{') {
// inner class
i = readClass(njpackage, njname, identifier, i - 1);
+ identifier = null;
} else if (c == ';') {
// field or method?
if (isMethod) {
@@ -283,7 +320,6 @@ public class PermissionParser {
} else {
njclass.fieldNames.add(identifier);
}
- isMethod = false;
identifier = null;
i += 1;
} else if (c == '(' && !isMethod) {
@@ -293,6 +329,10 @@ public class PermissionParser {
} else if (c == ')' && src.charAt(i - 1) == '(') {
i += 1;
} else if (c == '}') {
+ // restrict the whole class
+ if (njname != null && njclass.isEmpty()) {
+ njpackage.addNoJexl(njname, Permissions.NOJEXL_CLASS);
+ }
i += 1;
break;
} else {
diff --git a/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java b/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
index 6125ebf..64bf0d6 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
@@ -25,6 +25,7 @@ import org.apache.commons.jexl3.introspection.JexlPropertySet;
import org.apache.commons.jexl3.introspection.JexlUberspect;
import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -88,9 +89,9 @@ public class Uberspect implements JexlUberspect {
* @param perms the introspector permissions
*/
public Uberspect(final Log runtimeLogger, final JexlUberspect.ResolverStrategy sty, final Permissions perms) {
- logger = runtimeLogger;
+ logger = runtimeLogger == null? LogFactory.getLog(JexlEngine.class) : runtimeLogger;
strategy = sty == null? JexlUberspect.JEXL_STRATEGY : sty;
- permissions = perms;
+ permissions = perms == null? Permissions.DEFAULT : perms;
ref = new SoftReference<Introspector>(null);
loader = new SoftReference<ClassLoader>(getClass().getClassLoader());
operatorMap = new ConcurrentHashMap<Class<? extends JexlArithmetic>, Set<JexlOperator>>();
diff --git a/src/main/java/org/apache/commons/jexl3/introspection/JexlPermissions.java b/src/main/java/org/apache/commons/jexl3/introspection/JexlPermissions.java
new file mode 100644
index 0000000..0b21639
--- /dev/null
+++ b/src/main/java/org/apache/commons/jexl3/introspection/JexlPermissions.java
@@ -0,0 +1,65 @@
+/*
+ * 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.introspection;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * A JEXL dedicated 'security manager' that constraints which packages/classes/constructors/fields/methods
+ * are made visible to JEXL scripts.
+ */
+public interface JexlPermissions {
+ /**
+ * Checks whether a package explicitly disallows JEXL introspection.
+ * @param pack the package
+ * @return true if JEXL is allowed to introspect, false otherwise
+ */
+ boolean allow(final Package pack);
+
+ /**
+ * Checks whether a class or one of its super-classes or implemented interfaces
+ * explicitly disallows JEXL introspection.
+ * @param clazz the class to check
+ * @return true if JEXL is allowed to introspect, false otherwise
+ */
+ boolean allow(final Class<?> clazz);
+
+ /**
+ * Checks whether a constructor explicitly disallows JEXL introspection.
+ * @param ctor the constructor to check
+ * @return true if JEXL is allowed to introspect, false otherwise
+ */
+ boolean allow(final Constructor<?> ctor);
+
+ /**
+ * Checks whether a method explicitly disallows JEXL introspection.
+ * <p>Since methods can be overridden, this also checks that no superclass or interface
+ * explicitly disallows this methods.</p>
+ * @param method the method to check
+ * @return true if JEXL is allowed to introspect, false otherwise
+ */
+ boolean allow(final Method method);
+
+ /**
+ * Checks whether a field explicitly disallows JEXL introspection.
+ * @param field the field to check
+ * @return true if JEXL is allowed to introspect, false otherwise
+ */
+ boolean allow(final Field field);
+}
diff --git a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
index 7683d6a..d9f515a 100644
--- a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
+++ b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
@@ -22,6 +22,8 @@ import java.lang.reflect.Method;
import org.apache.commons.jexl3.internal.Util;
+import org.apache.commons.jexl3.internal.introspection.Permissions;
+import org.apache.commons.jexl3.internal.introspection.Uberspect;
import org.junit.After;
import org.junit.Assert;
@@ -67,7 +69,9 @@ public class JexlTestCase {
}
public static JexlEngine createEngine(final boolean lenient) {
- return new JexlBuilder().arithmetic(new JexlArithmetic(!lenient)).cache(128).create();
+ return new JexlBuilder()
+ .uberspect(new Uberspect(null, null, Permissions.SECURE))
+ .arithmetic(new JexlArithmetic(!lenient)).cache(128).create();
}
/**
diff --git a/src/test/java/org/apache/commons/jexl3/PropertyAccessTest.java b/src/test/java/org/apache/commons/jexl3/PropertyAccessTest.java
index cd12059..7883c0a 100644
--- a/src/test/java/org/apache/commons/jexl3/PropertyAccessTest.java
+++ b/src/test/java/org/apache/commons/jexl3/PropertyAccessTest.java
@@ -21,7 +21,11 @@ import java.util.Map;
import org.apache.commons.jexl3.internal.Debugger;
import org.apache.commons.jexl3.internal.introspection.IndexedType;
+import org.apache.commons.jexl3.internal.introspection.Permissions;
+import org.apache.commons.jexl3.internal.introspection.Uberspect;
+import org.apache.commons.jexl3.introspection.JexlUberspect;
import org.apache.commons.jexl3.junit.Asserter;
+import org.apache.commons.logging.LogFactory;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -379,7 +383,9 @@ public class PropertyAccessTest extends JexlTestCase {
final HashMap<Object, Object> x = new HashMap<Object, Object>();
x.put(2, "123456789");
ctx.set("x", x);
- final JexlEngine engine = new JexlBuilder().strict(true).silent(false).create();
+ final JexlEngine engine = new JexlBuilder()
+ .uberspect(new Uberspect(null, null, null))
+ .strict(true).silent(false).create();
String stmt = "x.2.class.name";
JexlScript script = engine.createScript(stmt);
Object result = script.execute(ctx);
@@ -551,4 +557,4 @@ public class PropertyAccessTest extends JexlTestCase {
Assert.assertEquals(42, result);
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/org/apache/commons/jexl3/internal/introspection/MiscIntrospectionTest.java b/src/test/java/org/apache/commons/jexl3/internal/introspection/MiscIntrospectionTest.java
index ab3aeb3..69549db 100644
--- a/src/test/java/org/apache/commons/jexl3/internal/introspection/MiscIntrospectionTest.java
+++ b/src/test/java/org/apache/commons/jexl3/internal/introspection/MiscIntrospectionTest.java
@@ -26,8 +26,10 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.Set;
/**
* Checks the CacheMap.MethodKey implementation
@@ -95,129 +97,4 @@ public class MiscIntrospectionTest {
Assert.assertEquals(-1, alw.indexOf(null));
}
- public static class A {
- public int i;
- public A() {}
- public int method() { return 0; }
- }
-
- @NoJexl
- public interface InterNoJexl0 {
- int method();
- }
-
- public interface InterNoJexl1 {
- @NoJexl
- int method();
- }
-
-
- public static class A0 extends A implements InterNoJexl0 {
- @NoJexl public int i0;
- @NoJexl public A0() {}
- @Override public int method() { return 1; }
- }
-
- public static class A1 extends A implements InterNoJexl1 {
- private int i1;
- @NoJexl public A1() {}
- @Override public int method() { return 2; }
- }
-
- @NoJexl
- public static class A2 extends A {
- public A2() {}
- @Override public int method() { return 3; }
- }
-
- protected static class A3 {
- protected int i3;
- protected A3() {}
- int method() { return 4; }
- }
-
- public static class A5 implements InterNoJexl5 {
- public A5() {}
- @Override public int method() { return 0; }
- }
-
- @NoJexl
- public interface InterNoJexl5 {
- int method();
- }
-
- @Test
- public void testPermissions() throws Exception {
- Permissions p = Permissions.DEFAULT;
- Assert.assertFalse(p.allow((Field) null));
- Assert.assertFalse(p.allow((Package) null));
- Assert.assertFalse(p.allow((Method) null));
- Assert.assertFalse(p.allow((Constructor<?>) null));
- Assert.assertFalse(p.allow((Class<?>) null));
-
- Assert.assertTrue(p.allow(A2.class));
- Assert.assertFalse(p.allow(A3.class));
- Assert.assertFalse(p.allow(A5.class));
-
- Method mA = A.class.getMethod("method");
- Assert.assertNotNull(mA);
- Method mA0 = A0.class.getMethod("method");
- Assert.assertNotNull(mA0);
- Method mA1 = A1.class.getMethod("method");
- Assert.assertNotNull(mA1);
- Method mA2 = A2.class.getMethod("method");
- Assert.assertNotNull(mA1);
- Method mA3 = A2.class.getDeclaredMethod("method");
- Assert.assertNotNull(mA1);
-
- Assert.assertTrue(p.allow(mA));
- Assert.assertFalse(p.allow(mA0));
- Assert.assertFalse(p.allow(mA1));
- Assert.assertFalse(p.allow(mA2));
- Assert.assertFalse(p.allow(mA3));
-
- Field fA = A.class.getField("i");
- Assert.assertNotNull(fA);
- Assert.assertTrue(p.allow(fA));
-
- Field fA0 = A0.class.getField("i0");
- Assert.assertNotNull(fA0);
- Assert.assertFalse(p.allow(fA0));
- Field fA1 = A1.class.getDeclaredField("i1");
- Assert.assertNotNull(fA1);
- Assert.assertFalse(p.allow(fA0));
-
- Constructor<?> cA = A.class.getConstructor();
- Assert.assertNotNull(cA);
- Assert.assertTrue(p.allow(cA));
-
- Constructor<?> cA0 = A0.class.getConstructor();
- Assert.assertNotNull(cA0);
- Assert.assertFalse(p.allow(cA0));
-
- Constructor<?> cA3 = A3.class.getDeclaredConstructor();
- Assert.assertNotNull(cA3);
- Assert.assertFalse(p.allow(cA3));
- }
-
-
- @Test
- public void testParsePermissions0() throws Exception {
- String src = "java.lang { Runtime { exit(); } }";
- PermissionParser pp = new PermissionParser();
- Map<String, Permissions.NoJexlPackage> nojexlmap = pp.parse(src);
- Assert.assertNotNull(nojexlmap);
- }
-
-
- @Test
- public void testParsePermissions1() throws Exception {
- String src = "java.lang { Runtime { exit(); } }" +
- "java.rmi {}" +
- "java.io { File {} }" +
- "java.nio { Path {} }";
- PermissionParser pp = new PermissionParser();
- Map<String, Permissions.NoJexlPackage> nojexlmap = pp.parse(src);
- Assert.assertNotNull(nojexlmap);
- }
}
diff --git a/src/test/java/org/apache/commons/jexl3/internal/introspection/MiscIntrospectionTest.java b/src/test/java/org/apache/commons/jexl3/internal/introspection/NoJexlTest.java
similarity index 64%
copy from src/test/java/org/apache/commons/jexl3/internal/introspection/MiscIntrospectionTest.java
copy to src/test/java/org/apache/commons/jexl3/internal/introspection/NoJexlTest.java
index ab3aeb3..02d12a3 100644
--- a/src/test/java/org/apache/commons/jexl3/internal/introspection/MiscIntrospectionTest.java
+++ b/src/test/java/org/apache/commons/jexl3/internal/introspection/NoJexlTest.java
@@ -16,7 +16,6 @@
*/
package org.apache.commons.jexl3.internal.introspection;
-import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.annotations.NoJexl;
import org.junit.Assert;
import org.junit.Test;
@@ -24,76 +23,16 @@ import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
-import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.Map;
-import java.util.NoSuchElementException;
+import java.util.Set;
/**
* Checks the CacheMap.MethodKey implementation
*/
-public class MiscIntrospectionTest {
- @Test
- public void testEmptyContext() {
- try {
- JexlEngine.EMPTY_CONTEXT.set("nope", 42);
- Assert.fail("empty context should be readonly");
- } catch(UnsupportedOperationException xun) {
- Assert.assertNotNull(xun);
- }
- }
- @Test
- public void testArrayIterator() {
- // not on lists
- try {
- new ArrayIterator(new ArrayList<>());
- } catch(IllegalArgumentException xill) {
- Assert.assertNotNull(xill);
- }
- // wih null?
- ArrayIterator ai0 = new ArrayIterator(null);
- Assert.assertFalse(ai0.hasNext());
- try {
- ai0.next();
- Assert.fail("should have failed");
- } catch(NoSuchElementException no) {
- Assert.assertNotNull(no);
- }
- // an array
- ai0 = new ArrayIterator(new int[]{42});
- Assert.assertTrue(ai0.hasNext());
- Assert.assertEquals(42, ai0.next());
- Assert.assertFalse(ai0.hasNext());
- try {
- ai0.next();
- Assert.fail("iterator on null?");
- } catch(NoSuchElementException no) {
- Assert.assertNotNull(no);
- }
- // no remove
- try {
- ai0.remove();
- Assert.fail("should have failed");
- } catch(UnsupportedOperationException no) {
- Assert.assertNotNull(no);
- }
- }
-
- @Test
- public void testArrayListWrapper() {
- ArrayListWrapper alw ;
- try {
- new ArrayListWrapper(1);
- Assert.fail("non-array wrap?");
- } catch(IllegalArgumentException xil) {
- Assert.assertNotNull(xil);
- }
- Integer[] ai = new Integer[]{1, 2};
- alw = new ArrayListWrapper(ai);
- Assert.assertEquals(1, alw.indexOf(2));
- Assert.assertEquals(-1, alw.indexOf(null));
- }
+public class NoJexlTest {
public static class A {
public int i;
@@ -155,9 +94,9 @@ public class MiscIntrospectionTest {
Assert.assertFalse(p.allow((Constructor<?>) null));
Assert.assertFalse(p.allow((Class<?>) null));
- Assert.assertTrue(p.allow(A2.class));
- Assert.assertFalse(p.allow(A3.class));
- Assert.assertFalse(p.allow(A5.class));
+ Assert.assertFalse(p.allow(A2.class));
+ Assert.assertTrue(p.allow(A3.class));
+ Assert.assertTrue(p.allow(A5.class));
Method mA = A.class.getMethod("method");
Assert.assertNotNull(mA);
@@ -166,9 +105,9 @@ public class MiscIntrospectionTest {
Method mA1 = A1.class.getMethod("method");
Assert.assertNotNull(mA1);
Method mA2 = A2.class.getMethod("method");
- Assert.assertNotNull(mA1);
+ Assert.assertNotNull(mA2);
Method mA3 = A2.class.getDeclaredMethod("method");
- Assert.assertNotNull(mA1);
+ Assert.assertNotNull(mA3);
Assert.assertTrue(p.allow(mA));
Assert.assertFalse(p.allow(mA0));
@@ -204,8 +143,8 @@ public class MiscIntrospectionTest {
@Test
public void testParsePermissions0() throws Exception {
String src = "java.lang { Runtime { exit(); } }";
- PermissionParser pp = new PermissionParser();
- Map<String, Permissions.NoJexlPackage> nojexlmap = pp.parse(src);
+ Permissions p = new PermissionsParser().parse(src);
+ Map<String, Permissions.NoJexlPackage> nojexlmap = p.getPackages();
Assert.assertNotNull(nojexlmap);
}
@@ -216,8 +155,20 @@ public class MiscIntrospectionTest {
"java.rmi {}" +
"java.io { File {} }" +
"java.nio { Path {} }";
- PermissionParser pp = new PermissionParser();
- Map<String, Permissions.NoJexlPackage> nojexlmap = pp.parse(src);
+ Permissions p = new PermissionsParser().parse(src);
+ Map<String, Permissions.NoJexlPackage> nojexlmap = p.getPackages();
Assert.assertNotNull(nojexlmap);
}
+
+ @Test
+ public void testWildCardPackages() {
+ Set<String> wildcards;
+ boolean found;
+ wildcards = new HashSet<>(Arrays.asList("com.apache.*"));
+ found = Permissions.wildcardAllow(wildcards, "com.apache.commons.jexl3");
+ Assert.assertTrue(found);
+ found = Permissions.wildcardAllow(wildcards, "com.google.spexl");
+ Assert.assertFalse(found);
+ }
+
}
diff --git a/src/test/java/org/apache/commons/jexl3/internal/introspection/MiscIntrospectionTest.java b/src/test/java/org/apache/commons/jexl3/internal/introspection/PermissionsTest.java
similarity index 59%
copy from src/test/java/org/apache/commons/jexl3/internal/introspection/MiscIntrospectionTest.java
copy to src/test/java/org/apache/commons/jexl3/internal/introspection/PermissionsTest.java
index ab3aeb3..f53006f 100644
--- a/src/test/java/org/apache/commons/jexl3/internal/introspection/MiscIntrospectionTest.java
+++ b/src/test/java/org/apache/commons/jexl3/internal/introspection/PermissionsTest.java
@@ -16,84 +16,22 @@
*/
package org.apache.commons.jexl3.internal.introspection;
-import org.apache.commons.jexl3.JexlEngine;
-import org.apache.commons.jexl3.annotations.NoJexl;
import org.junit.Assert;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
-import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.Map;
-import java.util.NoSuchElementException;
+import java.util.Set;
/**
* Checks the CacheMap.MethodKey implementation
*/
-public class MiscIntrospectionTest {
- @Test
- public void testEmptyContext() {
- try {
- JexlEngine.EMPTY_CONTEXT.set("nope", 42);
- Assert.fail("empty context should be readonly");
- } catch(UnsupportedOperationException xun) {
- Assert.assertNotNull(xun);
- }
- }
- @Test
- public void testArrayIterator() {
- // not on lists
- try {
- new ArrayIterator(new ArrayList<>());
- } catch(IllegalArgumentException xill) {
- Assert.assertNotNull(xill);
- }
- // wih null?
- ArrayIterator ai0 = new ArrayIterator(null);
- Assert.assertFalse(ai0.hasNext());
- try {
- ai0.next();
- Assert.fail("should have failed");
- } catch(NoSuchElementException no) {
- Assert.assertNotNull(no);
- }
- // an array
- ai0 = new ArrayIterator(new int[]{42});
- Assert.assertTrue(ai0.hasNext());
- Assert.assertEquals(42, ai0.next());
- Assert.assertFalse(ai0.hasNext());
- try {
- ai0.next();
- Assert.fail("iterator on null?");
- } catch(NoSuchElementException no) {
- Assert.assertNotNull(no);
- }
- // no remove
- try {
- ai0.remove();
- Assert.fail("should have failed");
- } catch(UnsupportedOperationException no) {
- Assert.assertNotNull(no);
- }
- }
-
- @Test
- public void testArrayListWrapper() {
- ArrayListWrapper alw ;
- try {
- new ArrayListWrapper(1);
- Assert.fail("non-array wrap?");
- } catch(IllegalArgumentException xil) {
- Assert.assertNotNull(xil);
- }
- Integer[] ai = new Integer[]{1, 2};
- alw = new ArrayListWrapper(ai);
- Assert.assertEquals(1, alw.indexOf(2));
- Assert.assertEquals(-1, alw.indexOf(null));
- }
+public class PermissionsTest {
public static class A {
public int i;
@@ -101,30 +39,29 @@ public class MiscIntrospectionTest {
public int method() { return 0; }
}
- @NoJexl
+ //@NoJexl
public interface InterNoJexl0 {
int method();
}
public interface InterNoJexl1 {
- @NoJexl
+ //@NoJexl
int method();
}
-
public static class A0 extends A implements InterNoJexl0 {
- @NoJexl public int i0;
- @NoJexl public A0() {}
+ /*@NoJexl*/ public int i0;
+ /*@NoJexl*/ public A0() {}
@Override public int method() { return 1; }
}
public static class A1 extends A implements InterNoJexl1 {
private int i1;
- @NoJexl public A1() {}
+ /*@NoJexl*/ public A1() {}
@Override public int method() { return 2; }
}
- @NoJexl
+ //@NoJexl
public static class A2 extends A {
public A2() {}
@Override public int method() { return 3; }
@@ -141,23 +78,33 @@ public class MiscIntrospectionTest {
@Override public int method() { return 0; }
}
- @NoJexl
+ //@NoJexl
public interface InterNoJexl5 {
int method();
}
@Test
public void testPermissions() throws Exception {
- Permissions p = Permissions.DEFAULT;
+
+ String src = " org.apache.commons.jexl3.internal.introspection { PermissionsTest { "+
+ "InterNoJexl0 { } "+
+ "InterNoJexl1 { method(); } "+
+ "A0 { A0(); i0; } "+
+ "A1 { A1(); } "+
+ "A2 { } "+
+ "InterNoJexl5 { } "+
+ "} }";
+
+ Permissions p = new PermissionsParser().parse(src);
Assert.assertFalse(p.allow((Field) null));
Assert.assertFalse(p.allow((Package) null));
Assert.assertFalse(p.allow((Method) null));
Assert.assertFalse(p.allow((Constructor<?>) null));
Assert.assertFalse(p.allow((Class<?>) null));
- Assert.assertTrue(p.allow(A2.class));
- Assert.assertFalse(p.allow(A3.class));
- Assert.assertFalse(p.allow(A5.class));
+ Assert.assertFalse(p.allow(A2.class));
+ Assert.assertTrue(p.allow(A3.class));
+ Assert.assertTrue(p.allow(A5.class));
Method mA = A.class.getMethod("method");
Assert.assertNotNull(mA);
@@ -166,9 +113,9 @@ public class MiscIntrospectionTest {
Method mA1 = A1.class.getMethod("method");
Assert.assertNotNull(mA1);
Method mA2 = A2.class.getMethod("method");
- Assert.assertNotNull(mA1);
+ Assert.assertNotNull(mA2);
Method mA3 = A2.class.getDeclaredMethod("method");
- Assert.assertNotNull(mA1);
+ Assert.assertNotNull(mA3);
Assert.assertTrue(p.allow(mA));
Assert.assertFalse(p.allow(mA0));
@@ -202,22 +149,53 @@ public class MiscIntrospectionTest {
@Test
- public void testParsePermissions0() throws Exception {
+ public void testParsePermissions0() {
String src = "java.lang { Runtime { exit(); } }";
- PermissionParser pp = new PermissionParser();
- Map<String, Permissions.NoJexlPackage> nojexlmap = pp.parse(src);
+ PermissionsParser pp = new PermissionsParser();
+ Permissions p = pp.parse(src);
+ Map<String, Permissions.NoJexlPackage> nojexlmap = p.getPackages();
Assert.assertNotNull(nojexlmap);
}
@Test
- public void testParsePermissions1() throws Exception {
+ public void testParsePermissions1() {
String src = "java.lang { Runtime { exit(); } }" +
"java.rmi {}" +
"java.io { File {} }" +
"java.nio { Path {} }";
- PermissionParser pp = new PermissionParser();
- Map<String, Permissions.NoJexlPackage> nojexlmap = pp.parse(src);
+ Permissions p = new PermissionsParser().parse(src);
+ Map<String, Permissions.NoJexlPackage> nojexlmap = p.getPackages();
Assert.assertNotNull(nojexlmap);
}
+
+ @Test
+ public void testWildCardPackages() {
+ Set<String> wildcards;
+ boolean found;
+ wildcards = new HashSet<>(Arrays.asList("com.apache.*"));
+ found = Permissions.wildcardAllow(wildcards, "com.apache.commons.jexl3");
+ Assert.assertTrue(found);
+ found = Permissions.wildcardAllow(wildcards, "com.google.spexl");
+ Assert.assertFalse(found);
+ }
+
+ @Test
+ public void testSecurePermissions() {
+ Permissions SECURE = new PermissionsParser().parse(
+ "java.lang.*\n"+
+ "java.math.*\n"+
+ "java.text.*\n"+
+ "java.util.*\n"+
+ "java.lang { Runtime {} System {} ProcessBuilder {} Class {} }\n" +
+ "java.lang.annotation {}\n" +
+ "java.lang.instrument {}\n" +
+ "java.lang.invoke {}\n" +
+ "java.lang.management {}\n" +
+ "java.lang.ref {}\n" +
+ "java.lang.reflect {}\n");
+ Assert.assertNotNull(SECURE);
+ Set<String> wilcards = SECURE.getWildcards();
+ Assert.assertEquals(4, wilcards.size());
+ }
}
diff --git a/src/test/java/org/apache/commons/jexl3/jexl342/OptionalTest.java b/src/test/java/org/apache/commons/jexl3/jexl342/OptionalTest.java
index 0aecde7..ea28e11 100644
--- a/src/test/java/org/apache/commons/jexl3/jexl342/OptionalTest.java
+++ b/src/test/java/org/apache/commons/jexl3/jexl342/OptionalTest.java
@@ -83,6 +83,10 @@ public class OptionalTest {
result = script.execute(null, thing);
Assert.assertNull(result);
thing.name = "froboz";
+ script = jexl.createScript(info,"thing.names", "thing");
+ result = script.execute(null, thing);
+ Assert.assertNotNull(result);
+ script = jexl.createScript(info,"thing.names.size()", "thing");
result = script.execute(null, thing);
Assert.assertEquals(1, result);
}