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 2021/06/04 16:05:45 UTC
[commons-jexl] branch master updated: JEXL-350: ensure null can be
allowed/blocked as a property to read/write in sandbox, tests, verbosity
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 be81fa5 JEXL-350: ensure null can be allowed/blocked as a property to read/write in sandbox, tests, verbosity
be81fa5 is described below
commit be81fa5325fadb28a51b0140da6eb17cb8538429
Author: henrib <he...@apache.org>
AuthorDate: Fri Jun 4 18:05:25 2021 +0200
JEXL-350: ensure null can be allowed/blocked as a property to read/write in sandbox, tests, verbosity
---
.../apache/commons/jexl3/internal/MapBuilder.java | 2 +-
.../internal/introspection/SandboxUberspect.java | 46 +++---
.../commons/jexl3/introspection/JexlSandbox.java | 25 +++-
.../java/org/apache/commons/jexl3/BuilderTest.java | 76 ++++++++++
.../java/org/apache/commons/jexl3/JXLTTest.java | 8 +-
.../apache/commons/jexl3/ParseFailuresTest.java | 10 +-
.../commons/jexl3/introspection/SandboxTest.java | 157 +++++++++++++++++++--
7 files changed, 283 insertions(+), 41 deletions(-)
diff --git a/src/main/java/org/apache/commons/jexl3/internal/MapBuilder.java b/src/main/java/org/apache/commons/jexl3/internal/MapBuilder.java
index b6aac4e..57b27e7 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/MapBuilder.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/MapBuilder.java
@@ -41,7 +41,7 @@ public class MapBuilder implements JexlArithmetic.MapBuilder {
}
@Override
- public Object create() {
+ public Map<Object,Object> create() {
return map;
}
}
diff --git a/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java b/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java
index afb94a8..fcf73ef 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java
@@ -78,7 +78,7 @@ public final class SandboxUberspect implements JexlUberspect {
} else {
className = null;
}
- return className != null? uberspect.getConstructor(className, args) : null;
+ return className != null && className != JexlSandbox.NULL ? uberspect.getConstructor(className, args) : null;
}
@Override
@@ -86,7 +86,7 @@ public final class SandboxUberspect implements JexlUberspect {
if (obj != null && method != null) {
final Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
final String actual = sandbox.execute(clazz, method);
- if (actual != null) {
+ if (actual != null && actual != JexlSandbox.NULL) {
return uberspect.getMethod(obj, actual, args);
}
}
@@ -107,13 +107,20 @@ public final class SandboxUberspect implements JexlUberspect {
public JexlPropertyGet getPropertyGet(final List<PropertyResolver> resolvers,
final Object obj,
final Object identifier) {
- if (obj != null && identifier != null) {
- final String property = identifier.toString();
- final String actual = sandbox.read(obj.getClass(), property);
- if (actual != null) {
- // no transformation, strict equality: use identifier before string conversion
- final Object pty = actual == property? identifier : actual;
- return uberspect.getPropertyGet(resolvers, obj, pty);
+ if (obj != null) {
+ if (identifier != null) {
+ final String property = identifier.toString();
+ final String actual = sandbox.read(obj.getClass(), property);
+ if (actual != null) {
+ // no transformation, strict equality: use identifier before string conversion
+ final Object pty = actual == property ? identifier : actual;
+ return uberspect.getPropertyGet(resolvers, obj, pty);
+ }
+ } else {
+ final String actual = sandbox.read(obj.getClass(), null);
+ if (actual != JexlSandbox.NULL) {
+ return uberspect.getPropertyGet(resolvers, obj, null);
+ }
}
}
return null;
@@ -129,13 +136,20 @@ public final class SandboxUberspect implements JexlUberspect {
final Object obj,
final Object identifier,
final Object arg) {
- if (obj != null && identifier != null) {
- final String property = identifier.toString();
- final String actual = sandbox.write(obj.getClass(), property);
- if (actual != null) {
- // no transformation, strict equality: use identifier before string conversion
- final Object pty = actual == property? identifier : actual;
- return uberspect.getPropertySet(resolvers, obj, pty, arg);
+ if (obj != null) {
+ if (identifier != null) {
+ final String property = identifier.toString();
+ final String actual = sandbox.write(obj.getClass(), property);
+ if (actual != null) {
+ // no transformation, strict equality: use identifier before string conversion
+ final Object pty = actual == property ? identifier : actual;
+ return uberspect.getPropertySet(resolvers, obj, pty, arg);
+ }
+ } else {
+ final String actual = sandbox.write(obj.getClass(), null);
+ if (actual != JexlSandbox.NULL) {
+ return uberspect.getPropertySet(resolvers, obj, null, arg);
+ }
}
}
return null;
diff --git a/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java b/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java
index f55aa64..62d521c 100644
--- a/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java
+++ b/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java
@@ -67,6 +67,10 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public final class JexlSandbox {
/**
+ * The marker string for explicitly disallowed null properties.
+ */
+ public static final String NULL = "?";
+ /**
* The map from class names to permissions.
*/
private final Map<String, Permissions> sandbox;
@@ -173,7 +177,7 @@ public final class JexlSandbox {
*
* @param clazz the class
* @param name the property name
- * @return null if not allowed, the name of the property to use otherwise
+ * @return null (or NULL if name is null) if not allowed, the name of the property to use otherwise
*/
public String read(final Class<?> clazz, final String name) {
return get(clazz).read().get(name);
@@ -196,7 +200,7 @@ public final class JexlSandbox {
*
* @param clazz the class
* @param name the property name
- * @return null if not allowed, the name of the property to use otherwise
+ * @return null (or NULL if name is null) if not allowed, the name of the property to use otherwise
*/
public String write(final Class<?> clazz, final String name) {
return get(clazz).write().get(name);
@@ -268,7 +272,7 @@ public final class JexlSandbox {
* Whether a given name is allowed or not.
*
* @param name the method/property name to check
- * @return null if not allowed, the actual name to use otherwise
+ * @return null (or NULL if name is null) if not allowed, the actual name to use otherwise
*/
public String get(final String name) {
return name;
@@ -313,7 +317,7 @@ public final class JexlSandbox {
@Override
public String get(final String name) {
- return null;
+ return name == null? NULL : null;
}
};
@@ -349,7 +353,15 @@ public final class JexlSandbox {
@Override
public String get(final String name) {
- return names == null ? name : names.get(name);
+ if (names == null) {
+ return name;
+ }
+ String actual = names.get(name);
+ // if null is not explicitly allowed, explicit null aka NULL
+ if (name == null && actual == null && !names.containsKey(null)) {
+ return JexlSandbox.NULL;
+ }
+ return actual;
}
}
@@ -377,7 +389,8 @@ public final class JexlSandbox {
@Override
public String get(final String name) {
- return names != null && !names.contains(name) ? name : null;
+ // if name is null and contained in set, explicit null aka NULL
+ return names != null && !names.contains(name) ? name : name != null? null : NULL;
}
}
diff --git a/src/test/java/org/apache/commons/jexl3/BuilderTest.java b/src/test/java/org/apache/commons/jexl3/BuilderTest.java
new file mode 100644
index 0000000..81fecc2
--- /dev/null
+++ b/src/test/java/org/apache/commons/jexl3/BuilderTest.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl3;
+
+import org.apache.commons.jexl3.internal.introspection.SandboxUberspect;
+import org.apache.commons.jexl3.introspection.JexlSandbox;
+import org.apache.commons.jexl3.introspection.JexlUberspect;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.nio.charset.Charset;
+
+/**
+ * Checking the builder basics.
+ */
+public class BuilderTest {
+ private static JexlBuilder builder() {
+ return new JexlBuilder();
+ }
+
+ @Test
+ public void testFlags() {
+ Assert.assertTrue(builder().antish(true).antish());
+ Assert.assertFalse(builder().antish(false).antish());
+ Assert.assertTrue(builder().cancellable(true).cancellable());
+ Assert.assertFalse(builder().cancellable(false).cancellable());
+ Assert.assertTrue(builder().safe(true).safe());
+ Assert.assertFalse(builder().safe(false).safe());
+ Assert.assertTrue(builder().silent(true).silent());
+ Assert.assertFalse(builder().silent(false).silent());
+ Assert.assertTrue(builder().lexical(true).lexical());
+ Assert.assertFalse(builder().lexical(false).lexical());
+ Assert.assertTrue(builder().lexicalShade(true).lexicalShade());
+ Assert.assertFalse(builder().lexicalShade(false).lexicalShade());
+ Assert.assertTrue(builder().silent(true).silent());
+ Assert.assertFalse(builder().silent(false).silent());
+ Assert.assertTrue(builder().strict(true).strict());
+ Assert.assertFalse(builder().strict(false).strict());
+ }
+
+ @Test
+ public void testValues() {
+ Assert.assertEquals(1, builder().collectMode(1).collectMode());
+ Assert.assertEquals(0, builder().collectMode(0).collectMode());
+ Assert.assertEquals(32, builder().cacheThreshold(32).cacheThreshold());
+ Assert.assertEquals(8, builder().stackOverflow(8).stackOverflow());
+ }
+
+ @Test
+ public void testOther() {
+ ClassLoader cls = getClass().getClassLoader().getParent();
+ Assert.assertEquals(cls, builder().loader(cls).loader());
+ Charset cs = Charset.forName("UTF16");
+ Assert.assertEquals(cs, builder().charset(cs).charset());
+ Assert.assertEquals(cs, builder().loader(cs).charset());
+ JexlUberspect u0 = builder().create().getUberspect();
+ JexlSandbox sandbox = new JexlSandbox();
+ JexlUberspect uberspect = new SandboxUberspect(u0, sandbox);
+ Assert.assertEquals(sandbox, builder().sandbox(sandbox).sandbox());
+ Assert.assertEquals(uberspect, builder().uberspect(uberspect).uberspect());
+ }
+}
diff --git a/src/test/java/org/apache/commons/jexl3/JXLTTest.java b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
index 8e4de30..75457e0 100644
--- a/src/test/java/org/apache/commons/jexl3/JXLTTest.java
+++ b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
@@ -366,7 +366,7 @@ public class JXLTTest extends JexlTestCase {
} catch (final JxltEngine.Exception xjexl) {
// expected
final String xmsg = xjexl.getMessage();
- LOGGER.warn(xmsg);
+ LOGGER.debug(xmsg);
}
}
@@ -380,7 +380,7 @@ public class JXLTTest extends JexlTestCase {
} catch (final JxltEngine.Exception xjexl) {
// expected
final String xmsg = xjexl.getMessage();
- LOGGER.warn(xmsg);
+ LOGGER.debug(xmsg);
}
}
@@ -395,7 +395,7 @@ public class JXLTTest extends JexlTestCase {
} catch (final JxltEngine.Exception xjexl) {
// expected
final String xmsg = xjexl.getMessage();
- LOGGER.warn(xmsg);
+ LOGGER.debug(xmsg);
}
}
@@ -409,7 +409,7 @@ public class JXLTTest extends JexlTestCase {
} catch (final JxltEngine.Exception xjexl) {
// expected
final String xmsg = xjexl.getMessage();
- LOGGER.warn(xmsg);
+ LOGGER.debug(xmsg);
}
}
diff --git a/src/test/java/org/apache/commons/jexl3/ParseFailuresTest.java b/src/test/java/org/apache/commons/jexl3/ParseFailuresTest.java
index 8c1ada7..643e54c 100644
--- a/src/test/java/org/apache/commons/jexl3/ParseFailuresTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ParseFailuresTest.java
@@ -50,7 +50,7 @@ public class ParseFailuresTest extends JexlTestCase {
+ "\" should result in a JexlException");
} catch (final JexlException pe) {
// expected
- LOGGER.info(pe);
+ LOGGER.debug(pe);
}
}
@@ -64,7 +64,7 @@ public class ParseFailuresTest extends JexlTestCase {
+ "\" should result in a JexlException");
} catch (final JexlException pe) {
// expected
- LOGGER.info(pe);
+ LOGGER.debug(pe);
}
}
@@ -78,7 +78,7 @@ public class ParseFailuresTest extends JexlTestCase {
+ "\" should result in a JexlException");
} catch (final JexlException pe) {
// expected
- LOGGER.info(pe);
+ LOGGER.debug(pe);
}
}
@@ -92,7 +92,7 @@ public class ParseFailuresTest extends JexlTestCase {
+ "\" should result in a JexlException");
} catch (final JexlException pe) {
// expected
- LOGGER.info(pe);
+ LOGGER.debug(pe);
}
}
@@ -106,7 +106,7 @@ public class ParseFailuresTest extends JexlTestCase {
+ "\" should result in a JexlException");
} catch (final JexlException pe) {
// expected
- LOGGER.error(pe);
+ LOGGER.debug(pe);
}
}
diff --git a/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java b/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java
index 24190c4..2634281 100644
--- a/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java
+++ b/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java
@@ -17,16 +17,23 @@
package org.apache.commons.jexl3.introspection;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.jexl3.JexlArithmetic;
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlException;
+import org.apache.commons.jexl3.JexlExpression;
import org.apache.commons.jexl3.JexlScript;
import org.apache.commons.jexl3.JexlTestCase;
import org.apache.commons.jexl3.MapContext;
import org.apache.commons.jexl3.annotations.NoJexl;
+import org.apache.commons.jexl3.internal.introspection.Permissions;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -139,7 +146,7 @@ public class SandboxTest extends JexlTestCase {
Assert.fail("ctor should not be accessible");
} catch (final JexlException.Method xmethod) {
// ok, ctor should not have been accessible
- LOGGER.info(xmethod.toString());
+ LOGGER.debug(xmethod.toString());
}
}
@@ -162,7 +169,7 @@ public class SandboxTest extends JexlTestCase {
Assert.fail("Quux should not be accessible");
} catch (final JexlException.Method xmethod) {
// ok, Quux should not have been accessible
- LOGGER.info(xmethod.toString());
+ LOGGER.debug(xmethod.toString());
}
}
@@ -185,7 +192,7 @@ public class SandboxTest extends JexlTestCase {
Assert.fail("alias should not be accessible");
} catch (final JexlException.Property xvar) {
// ok, alias should not have been accessible
- LOGGER.info(xvar.toString());
+ LOGGER.debug(xvar.toString());
}
}
@@ -208,7 +215,7 @@ public class SandboxTest extends JexlTestCase {
Assert.fail("alias should not be accessible");
} catch (final JexlException.Property xvar) {
// ok, alias should not have been accessible
- LOGGER.info(xvar.toString());
+ LOGGER.debug(xvar.toString());
}
}
@@ -289,7 +296,7 @@ public class SandboxTest extends JexlTestCase {
Assert.fail("should have not been possible");
} catch (JexlException.Method | JexlException.Property xjm) {
// ok
- LOGGER.info(xjm.toString());
+ LOGGER.debug(xjm.toString());
}
}
}
@@ -353,7 +360,7 @@ public class SandboxTest extends JexlTestCase {
result = script.execute(context);
Assert.fail("should not allow calling exit!");
} catch (final JexlException xjexl) {
- LOGGER.info(xjexl.toString());
+ LOGGER.debug(xjexl.toString());
}
script = sjexl.createScript("System.exit(1)");
@@ -361,7 +368,7 @@ public class SandboxTest extends JexlTestCase {
result = script.execute(context);
Assert.fail("should not allow calling exit!");
} catch (final JexlException xjexl) {
- LOGGER.info(xjexl.toString());
+ LOGGER.debug(xjexl.toString());
}
script = sjexl.createScript("new('java.io.File', '/tmp/should-not-be-created')");
@@ -369,7 +376,7 @@ public class SandboxTest extends JexlTestCase {
result = script.execute(context);
Assert.fail("should not allow creating a file");
} catch (final JexlException xjexl) {
- LOGGER.info(xjexl.toString());
+ LOGGER.debug(xjexl.toString());
}
expr = "System.currentTimeMillis()";
@@ -450,7 +457,7 @@ public class SandboxTest extends JexlTestCase {
Assert.fail("should not be possible");
} catch (final JexlException xjm) {
// ok
- LOGGER.info(xjm.toString());
+ LOGGER.debug(xjm.toString());
}
}
@@ -488,4 +495,136 @@ public class SandboxTest extends JexlTestCase {
Assert.assertNotNull(xany);
}
}
+
+ @Test
+ public void testGetNullKeyAllowed0() throws Exception {
+ JexlEngine jexl = new JexlBuilder().sandbox(new JexlSandbox(true)).create();
+ JexlExpression expression = jexl.createExpression("{null : 'foo'}[null]");
+ Object o = expression.evaluate(null);
+ Assert.assertEquals("foo", o);
+ }
+
+ @Test
+ public void testGetNullKeyAllowed1() throws Exception {
+ JexlSandbox sandbox = new JexlSandbox(true, true);
+ JexlSandbox.Permissions p = sandbox.permissions("java.util.Map", false, true, true);
+ p.read().add("quux");
+ JexlEngine jexl = new JexlBuilder().sandbox(sandbox).create();
+ // cant read quux
+ String q = "'quux'"; //quotes are important!
+ JexlExpression expression = jexl.createExpression("{"+q+" : 'foo'}["+q+"]");
+ try {
+ Object o = expression.evaluate(null);
+ Assert.fail("should have blocked " + q);
+ } catch (JexlException.Property xp) {
+ Assert.assertTrue(xp.getMessage().contains("undefined"));
+ }
+ // can read foo, null
+ for(String k : Arrays.asList("'foo'", "null")) {
+ expression = jexl.createExpression("{"+k+" : 'foo'}["+k+"]");
+ Object o = expression.evaluate(null);
+ Assert.assertEquals("foo", o);
+ }
+ }
+
+ @Test
+ public void testGetNullKeyBlocked() throws Exception {
+ JexlSandbox sandbox = new JexlSandbox(true, true);
+ JexlSandbox.Permissions p = sandbox.permissions("java.util.Map", false, true, true);
+ p.read().add(null);
+ p.read().add("quux");
+ // can read bar
+ JexlEngine jexl = new JexlBuilder().sandbox(sandbox).create();
+ JexlExpression e0 = jexl.createExpression("{'bar' : 'foo'}['bar']");
+ Object r0 = e0.evaluate(null);
+ Assert.assertEquals("foo", r0);
+ // can not read quux, null
+ for(String k : Arrays.asList("'quux'", "null")) {
+ JexlExpression expression = jexl.createExpression("{"+k+" : 'foo'}["+k+"]");
+ try {
+ Object o = expression.evaluate(null);
+ Assert.fail("should have blocked " + k);
+ } catch (JexlException.Property xp) {
+ Assert.assertTrue(xp.getMessage().contains("undefined"));
+ }
+ }
+ }
+
+ public static class Arithmetic350 extends JexlArithmetic {
+ // cheat and keep the map builder around
+ MapBuilder mb = new org.apache.commons.jexl3.internal.MapBuilder(3);
+ public Arithmetic350(boolean astrict) {
+ super(astrict);
+ }
+ public MapBuilder mapBuilder(final int size) {
+ return mb;
+ }
+ Map<?,?> getLastMap() {
+ return (Map<Object,Object>) mb.create();
+ }
+ }
+
+ @Test
+ public void testSetNullKeyAllowed0() throws Exception {
+ Arithmetic350 a350 = new Arithmetic350(true);
+ JexlEngine jexl = new JexlBuilder().arithmetic(a350).sandbox(new JexlSandbox(true)).create();
+ JexlContext jc = new MapContext();
+ JexlExpression expression = jexl.createExpression("{null : 'foo'}[null] = 'bar'");
+ expression.evaluate(jc);
+ Map<?,?> map = a350.getLastMap();
+ Assert.assertEquals("bar", map.get(null));
+ }
+
+ @Test
+ public void testSetNullKeyAllowed1() throws Exception {
+ Arithmetic350 a350 = new Arithmetic350(true);
+ JexlSandbox sandbox = new JexlSandbox(true, true);
+ JexlSandbox.Permissions p = sandbox.permissions("java.util.Map", true, false, true);
+ p.write().add("quux");
+ JexlEngine jexl = new JexlBuilder().arithmetic(a350).sandbox(sandbox).create();
+ // can not write quux
+ String q = "'quux'"; //quotes are important!
+ JexlExpression expression = jexl.createExpression("{"+q+" : 'foo'}["+q+"] = '42'");
+ try {
+ Object o = expression.evaluate(null);
+ Assert.fail("should have blocked " + q);
+ } catch (JexlException.Property xp) {
+ Assert.assertTrue(xp.getMessage().contains("undefined"));
+ }
+ // can write bar, null
+ expression = jexl.createExpression("{'bar' : 'foo'}['bar'] = '42'");
+ expression.evaluate(null);
+ Map<?, ?> map = a350.getLastMap();
+ Assert.assertEquals("42", map.get("bar"));
+ map.clear();
+ expression = jexl.createExpression("{null : 'foo'}[null] = '42'");
+ expression.evaluate(null);
+ map = a350.getLastMap();
+ Assert.assertEquals("42", map.get(null));
+ }
+
+ @Test
+ public void testSetNullKeyBlocked() throws Exception {
+ Arithmetic350 a350 = new Arithmetic350(true);
+ JexlSandbox sandbox = new JexlSandbox(true, true);
+ JexlSandbox.Permissions p = sandbox.permissions("java.util.Map", true, false, true);
+ p.write().add(null);
+ p.write().add("quux");
+ JexlEngine jexl = new JexlBuilder().arithmetic(a350).sandbox(sandbox).create();
+ // can write bar
+ JexlExpression expression = jexl.createExpression("{'bar' : 'foo'}['bar'] = '42'");
+ expression.evaluate(null);
+ Map<?,?> map = a350.getLastMap();
+ Assert.assertEquals("42", map.get("bar"));
+ // can not write quux, null
+ for(String k : Arrays.asList("'quux'", "null")) {
+ expression = jexl.createExpression("{"+k+" : 'foo'}["+k+"] = '42'");
+ try {
+ Object o = expression.evaluate(null);
+ Assert.fail("should have blocked " + k);
+ } catch (JexlException.Property xp) {
+ Assert.assertTrue(xp.getMessage().contains("undefined"));
+ }
+ }
+ }
}