Skip to content
Permalink
Browse files
JEXL-357: exposing parser;
-adding tests;
  • Loading branch information
henrib committed Feb 4, 2022
1 parent 7fa0190 commit d6df444c1bca2c3f5d648666d94e92654f676c15
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 83 deletions.
@@ -472,7 +472,7 @@ protected Interpreter createInterpreter(final JexlContext context, final Frame f

@Override
public Script createExpression(final JexlInfo info, final String expression) {
return createScript(expressionFeatures, info, expression, null);
return createScript(expressionFeatures, info, expression);
}

@Override
@@ -71,6 +71,8 @@ static class NoJexlPackage {
this(new ConcurrentHashMap<>());
}

boolean isEmpty() { return nojexl.isEmpty(); }

@Override
public boolean equals(Object o) {
return o == this;
@@ -232,6 +232,10 @@ private void readPackages() {
}
} else {
if (c == '}') {
// empty means whole package
if (njpackage.isEmpty()) {
packages.put(pname, Permissions.NOJEXL_PACKAGE);
}
njpackage = null; // can restart anew
pname = null;
i += 1;
@@ -276,6 +280,15 @@ private int readClass(Permissions.NoJexlPackage njpackage, String outer, String
i = readEol(i + 1);
continue;
}
// end of class ?
if (njclass != null && c == '}') {
// restrict the whole class
if (njclass.isEmpty()) {
njpackage.addNoJexl(njname, Permissions.NOJEXL_CLASS);
}
i += 1;
break;
}
// read an identifier, the class name
if (identifier == null) {
int next = readIdentifier(temp, i);
@@ -284,25 +297,17 @@ private int readClass(Permissions.NoJexlPackage njpackage, String outer, String
temp.setLength(0);
i = next;
continue;
} else if (c == '}') {
i += 1;
// restrict the whole class
if (njname != null && njclass.isEmpty()) {
njpackage.addNoJexl(njname, Permissions.NOJEXL_CLASS);
}
break;
}
}
// parse a class:
if (njclass == null) {
// we must have read the class ('identifier {'...)
if (c == '{') {
if (identifier != null && c == '{') {
// if we have a class, it has a name
njclass = new Permissions.NoJexlClass();
njname = outer != null ? outer + "$" + identifier : identifier;
njpackage.addNoJexl(njname, njclass);
identifier = null;
i += 1;
} else {
throw new IllegalStateException(unexpected(c, i));
}
@@ -312,7 +317,9 @@ private int readClass(Permissions.NoJexlPackage njpackage, String outer, String
// inner class
i = readClass(njpackage, njname, identifier, i - 1);
identifier = null;
} else if (c == ';') {
continue;
}
if (c == ';') {
// field or method?
if (isMethod) {
njclass.methodNames.add(identifier);
@@ -321,26 +328,15 @@ private int readClass(Permissions.NoJexlPackage njpackage, String outer, String
njclass.fieldNames.add(identifier);
}
identifier = null;
i += 1;
} else if (c == '(' && !isMethod) {
// method; only one opening parenthesis allowed
isMethod = true;
i += 1;
} 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 {
} else if (c != ')' || src.charAt(i - 1) != '(') {
// closing parenthesis following opening one was expected
throw new IllegalStateException(unexpected(c, i));
}
} else {
i += 1;
}
i += 1;
}
return i;
}
@@ -16,6 +16,8 @@
*/
package org.apache.commons.jexl3.introspection;

import org.apache.commons.jexl3.internal.introspection.PermissionsParser;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -62,4 +64,13 @@ public interface JexlPermissions {
* @return true if JEXL is allowed to introspect, false otherwise
*/
boolean allow(final Field field);

/**
* Parses a set of permissions.
* @param src the permissions source
* @return the permissions instance
*/
static JexlPermissions parse(String... src) {
return new PermissionsParser().parse(src);
}
}
@@ -17,6 +17,7 @@
package org.apache.commons.jexl3.internal.introspection;

import org.apache.commons.jexl3.annotations.NoJexl;
import org.apache.commons.jexl3.introspection.JexlPermissions;
import org.junit.Assert;
import org.junit.Test;

@@ -86,7 +87,7 @@ public interface InterNoJexl5 {
}

@Test
public void testPermissions() throws Exception {
public void testNoJexlPermissions() throws Exception {
Permissions p = Permissions.DEFAULT;
Assert.assertFalse(p.allow((Field) null));
Assert.assertFalse(p.allow((Package) null));
@@ -139,36 +140,4 @@ public void testPermissions() throws Exception {
Assert.assertFalse(p.allow(cA3));
}


@Test
public void testParsePermissions0() throws Exception {
String src = "java.lang { Runtime { exit(); } }";
Permissions p = new PermissionsParser().parse(src);
Map<String, Permissions.NoJexlPackage> nojexlmap = p.getPackages();
Assert.assertNotNull(nojexlmap);
}


@Test
public void testParsePermissions1() throws Exception {
String src = "java.lang { Runtime { exit(); } }" +
"java.rmi {}" +
"java.io { File {} }" +
"java.nio { Path {} }";
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);
}

}
@@ -16,17 +16,24 @@
*/
package org.apache.commons.jexl3.internal.introspection;

import org.apache.commons.jexl3.internal.introspection.nojexlpackage.Invisible;
import org.apache.commons.jexl3.introspection.JexlPermissions;
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.math.BigDecimal;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import static org.apache.commons.jexl3.internal.introspection.Permissions.SECURE;

/**
* Checks the CacheMap.MethodKey implementation
*/
@@ -85,7 +92,6 @@ public interface InterNoJexl5 {

@Test
public void testPermissions() throws Exception {

String src = " org.apache.commons.jexl3.internal.introspection { PermissionsTest { "+
"InterNoJexl0 { } "+
"InterNoJexl1 { method(); } "+
@@ -95,7 +101,7 @@ public void testPermissions() throws Exception {
"InterNoJexl5 { } "+
"} }";

Permissions p = new PermissionsParser().parse(src);
JexlPermissions p = (Permissions) JexlPermissions.parse(src);
Assert.assertFalse(p.allow((Field) null));
Assert.assertFalse(p.allow((Package) null));
Assert.assertFalse(p.allow((Method) null));
@@ -147,26 +153,71 @@ public void testPermissions() throws Exception {
Assert.assertFalse(p.allow(cA3));
}

static Method getMethod(Class<?> clazz, String method) {
return Arrays.stream(clazz.getMethods()).filter(mth->mth.getName().equals(method)).findFirst().get();
}

@Test
public void testParsePermissions0() {
String src = "java.lang { Runtime { exit(); } }";
PermissionsParser pp = new PermissionsParser();
Permissions p = pp.parse(src);
public void testParsePermissions0() throws Exception {
String src = "java.lang { Runtime { exit(); exec(); } }";
Permissions p = (Permissions) JexlPermissions.parse(src);
Map<String, Permissions.NoJexlPackage> nojexlmap = p.getPackages();
Assert.assertNotNull(nojexlmap);
Permissions.NoJexlPackage njp = nojexlmap.get("java.lang");
Assert.assertNotNull(njp);
Method exit = getMethod(java.lang.Runtime.class,"exit");
Assert.assertNotNull(exit);
Assert.assertFalse(p.allow(exit));
Method exec = getMethod(java.lang.Runtime.class,"exec");
Assert.assertNotNull(exec);
Assert.assertFalse(p.allow(exec));
}

public static class Outer {
public static class Inner {
public void callMeNot() {}
}
}

@Test
public void testParsePermissions1() {
String src = "java.lang { Runtime { exit(); } }" +
"java.rmi {}" +
"java.io { File {} }" +
"java.nio { Path {} }";
Permissions p = new PermissionsParser().parse(src);
String[] src = new String[]{
"java.lang.*",
"java.math.*",
"java.text.*",
"java.util.*",
"java.lang { Runtime {} }",
"java.rmi {}",
"java.io { File {} }",
"java.nio { Path {} }" ,
"org.apache.commons.jexl3.internal.introspection { " +
"PermissionsTest { #level 0\n" +
" Outer { #level 1\n" +
" Inner { #level 2\n" +
" callMeNot();" +
" }" +
" }" +
" }" +
" }"};
Permissions p = (Permissions) JexlPermissions.parse(src);
Map<String, Permissions.NoJexlPackage> nojexlmap = p.getPackages();
Assert.assertNotNull(nojexlmap);
Set<String> wildcards = p.getWildcards();
Assert.assertEquals(4, wildcards.size());

Method exit = getMethod(java.lang.Runtime.class,"exit");
Assert.assertNotNull(exit);
Assert.assertFalse(p.allow(exit));
Method exec = getMethod(java.lang.Runtime.class,"getRuntime");
Assert.assertNotNull(exec);
Assert.assertFalse(p.allow(exec));
Method callMeNot = getMethod(Outer.Inner.class, "callMeNot");
Assert.assertNotNull(callMeNot);
Assert.assertFalse(p.allow(callMeNot));
Method uncallable = getMethod(Invisible.class, "uncallable");
Assert.assertFalse(p.allow(uncallable));
Package ip = Invisible.class.getPackage();
Assert.assertFalse(p.allow(ip));
}

@Test
@@ -182,20 +233,29 @@ public void testWildCardPackages() {

@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());
List<Class<?>> acs = Arrays.asList(
java.lang.Runtime.class,
java.math.BigDecimal.class,
java.text.SimpleDateFormat.class,
java.util.Map.class);
for(Class<?> ac: acs) {
Package p = ac.getPackage();
Assert.assertNotNull(ac.getName(), p);
Assert.assertTrue(ac.getName(), SECURE.allow(p));
}
List<Class<?>> nacs = Arrays.asList(
java.lang.annotation.ElementType.class,
java.lang.instrument.ClassDefinition.class,
java.lang.invoke.CallSite.class,
java.lang.management.BufferPoolMXBean.class,
java.lang.ref.SoftReference.class,
java.lang.reflect.Method.class);
for(Class<?> nac : nacs) {
Package p = nac.getPackage();
Assert.assertNotNull(nac.getName(), p);
Assert.assertFalse(nac.getName(), SECURE.allow(p));
}
}

}
@@ -0,0 +1,21 @@
/*
* 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.internal.introspection.nojexlpackage;

public class Invisible {
public String uncallable() { return "!"; }
}

0 comments on commit d6df444

Please sign in to comment.