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)

Reply via email to