David Costanzo created JEXL-350:
-----------------------------------
Summary: map[null] throws "unsolvable property" when a Sandbox is
used
Key: JEXL-350
URL: https://issues.apache.org/jira/browse/JEXL-350
Project: Commons JEXL
Issue Type: Improvement
Affects Versions: 3.1
Environment: jdk-11.0.5_10-hotspot
JEXL git pull on 2021-06-03
Reporter: David Costanzo
In JEXL 3.2 SNAPSHOT, you can can access a null property in a map if you have
no sandbox, but you cannot access a null property in a map if you have an
"allow box" sandbox. This asymmetry is weird and might be an oversight.
The fix for JEXL-327 allows setting a null property in a map using the array
syntax, but only for the case where there is no sandbox. The fix for JEXL-291
allows array-style access to values in a map when using a sandbox. What
remains is the intersection of these two use cases: accessing a null property
in a map using the array-access syntax when there's a sandbox.
*Impact:*
In my domain (working with data from clinical trials), a null numeric value
means "missing", which is a valid value. Our JEXL programmers implement
"switch" statements using a map, so you might see an expression that translates
a coded variable named "DMSEX" into an English description like:
{code:java}
{
null : "Unknown",
1 : "MALE",
2 : "FEMALE"
}[DMSEX]{code}
Not being able to read the "Unknown" value when DMSEX=null is a problem that
we've had to work around by copying the internal class SandboxUberspect and
hacking it to allow this.
*Technical Details:*
I think the problem starts in SandboxUbserspect.getPropertyGet() because
identifier is null. In this case, the most of the logic is skipped and null is
returned, indicating that the property is undefined.
{code:java}
@Override
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);
}
}
return null;
}
{code}
In my superficial understanding, the Sandbox.read() method doesn't have a way
to distinguish between "null is the property you want to read" and "access is
blocked" so in my hack, I had to always allow read access of null.
*Steps to Reproduce:*
Below are some unit tests which show the four possibilities of with/without
sandbox and get/set null. I've included the behavior in JEXL 3.1 and a recent
build of github as comments.
{code:java}
@Test
public void testGetNullKeyWithNoSandbox() throws Exception {
JexlEngine jexl = new JexlBuilder().create();
JexlContext jc = new MapContext();
JexlExpression expression = jexl.createExpression("{null : 'foo'}[null]");
// JEXL 3.1, works
// JEXL 3.2, works
Object o = expression.evaluate(jc);
Assert.assertEquals("foo", o);
}
@Test
public void testGetNullKeyWithSandbox() throws Exception {
JexlEngine jexl = new JexlBuilder().sandbox(new JexlSandbox(true)).create();
JexlContext jc = new MapContext();
JexlExpression expression = jexl.createExpression("{null : 'foo'}[null]");
// JEXL 3.1, throws JexlException$Property "unsolvable property
'<?>.<null>'"
// JEXL 3.2, throws JexlException$Property "undefined property '<?>.<null>'"
Object o = expression.evaluate(jc);
Assert.assertEquals("foo", o);
}
@Test
public void testSetNullKeyWithNoSandbox() throws Exception {
JexlEngine jexl = new JexlBuilder().create();
JexlContext jc = new MapContext();
JexlExpression expression = jexl.createExpression("{null : 'foo'}[null] =
'bar'");
// JEXL 3.1, throws JexlException$Property "unsolvable property
'<?>.<null>'"
// JEXL 3.2, works
expression.evaluate(jc);
}
@Test
public void testSetNullKeyWithSandbox() throws Exception {
JexlEngine jexl = new JexlBuilder().sandbox(new JexlSandbox(true)).create();
JexlContext jc = new MapContext();
JexlExpression expression = jexl.createExpression("{null : 'foo'}[null] =
'bar'");
// JEXL 3.1, throws JexlException$Property "unsolvable property
'<?>.<null>'"
// JEXL 3.2, throws JexlException$Property "undefined property '<?>.<null>'"
expression.evaluate(jc);
}{code}
--
This message was sent by Atlassian Jira
(v8.3.4#803005)