[ https://issues.apache.org/jira/browse/JEXL-350?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Henri Biestro resolved JEXL-350. -------------------------------- Resolution: Fixed Added to hotfix train. > 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 > Assignee: Henri Biestro > Priority: Major > Fix For: 3.2.1 > > > 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)