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 78b3dd90 JEXL-424: fully resolve sandbox inheritable permissions through super-interfaces and super-classes (vs cache lookup); - added test; 78b3dd90 is described below commit 78b3dd90f07d86531c8c99f6f9b5bea00bea8205 Author: Henri Biestro <hbies...@cloudera.com> AuthorDate: Tue Jun 18 17:52:26 2024 +0200 JEXL-424: fully resolve sandbox inheritable permissions through super-interfaces and super-classes (vs cache lookup); - added test; --- .../commons/jexl3/introspection/JexlSandbox.java | 56 ++++++++++++++++------ .../commons/jexl3/introspection/SandboxTest.java | 37 ++++++++++++++ 2 files changed, 78 insertions(+), 15 deletions(-) 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 241e3410..cae92e27 100644 --- a/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java +++ b/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java @@ -20,6 +20,7 @@ package org.apache.commons.jexl3.introspection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -67,7 +68,7 @@ import java.util.concurrent.ConcurrentHashMap; */ public final class JexlSandbox { /** - * A allow set of names. + * An allow set of names. */ static class AllowSet extends Names { /** The map of controlled names and aliases. */ @@ -108,12 +109,19 @@ public final class JexlSandbox { } return actual; } + + @Override + public String toString() { + return "allow{" + (names == null? "all" : Objects.toString(names.entrySet())) + "}"; + } } + /** * @deprecated since 3.2, use {@link BlockSet} */ @Deprecated public static final class BlackSet extends BlockSet {} + /** * A block set of names. */ @@ -141,12 +149,17 @@ public final class JexlSandbox { // if name is null and contained in set, explicit null aka NULL return names != null && !names.contains(name) ? name : name != null ? null : NULL; } + + @Override + public String toString() { + return "block{" + (names == null? "all" :Objects.toString(names)) + "}"; + } } + /** * A base set of names. */ public abstract static class Names { - /** * Adds a name to this set. * @@ -259,7 +272,7 @@ public final class JexlSandbox { } /** - * @return whether these permissions applies to derived classes. + * @return whether these permissions apply to derived classes. */ public boolean isInheritable() { return inheritable; @@ -331,8 +344,8 @@ public final class JexlSandbox { } @Override - protected Names copy() { - return this; + public String toString() { + return "allowAll"; } }; @@ -346,13 +359,13 @@ public final class JexlSandbox { } @Override - protected Names copy() { - return this; + public String get(final String name) { + return name == null ? NULL : null; } @Override - public String get(final String name) { - return name == null ? NULL : null; + public String toString() { + return "blockAll"; } }; @@ -533,12 +546,23 @@ public final class JexlSandbox { */ @SuppressWarnings("null") // clazz can not be null since permissions would be not null and block; public Permissions get(final Class<?> clazz) { - Permissions permissions = clazz == null ? BLOCK_ALL : sandbox.get(clazz.getName()); + // we only store the result for classes we actively seek permissions for + return clazz == null ? BLOCK_ALL : compute(clazz, true); + } + + /** + * Computes and optionally the permissions associated to a class. + * @param clazz the class + * @param store whether the resulting permissions should be stored in the sandbox + * @return the permissions + */ + private Permissions compute(final Class<?> clazz, boolean store) { + Permissions permissions = sandbox.get(clazz.getName()); if (permissions == null) { if (inherit) { // find first inherited interface that defines permissions for (final Class<?> inter : clazz.getInterfaces()) { - permissions = sandbox.get(inter.getName()); + permissions = compute(inter, false); if (permissions != null) { if (permissions.isInheritable()) { break; @@ -548,11 +572,11 @@ public final class JexlSandbox { } // nothing defined yet, find first superclass that defines permissions if (permissions == null) { - // lets walk all super classes + // let's walk all super classes Class<?> zuper = clazz.getSuperclass(); // walk all superclasses while (zuper != null) { - permissions = sandbox.get(zuper.getName()); + permissions = compute(zuper, false); if (permissions != null) { if (permissions.isInheritable()) { break; @@ -566,8 +590,10 @@ public final class JexlSandbox { if (permissions == null) { permissions = allow ? ALLOW_ALL : BLOCK_ALL; } - // store the info to avoid doing this costly look up - sandbox.put(clazz.getName(), permissions); + // store the info to avoid doing this costly look-up + if (store) { + sandbox.put(clazz.getName(), permissions); + } } else { permissions = allow ? ALLOW_ALL : BLOCK_ALL; } 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 0ad2a696..8f26f40f 100644 --- a/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java +++ b/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java @@ -24,6 +24,7 @@ import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -602,4 +603,40 @@ public class SandboxTest extends JexlTestCase { .contains("undefined")); } } + interface I{} + static class A implements I{} + static class B extends A{} + + @Test + public void testPermission0() { + JexlSandbox sandbox = new JexlSandbox(false, true); + sandbox.permissions(I.class.getName(), true, true, true, false); + System.out.println("permission A=" + sandbox.get(A.class.getName()).write()); + System.out.println("permission B=" + sandbox.get(B.class.getName()).write()); + } + @Test + public void testPermission1() { + JexlSandbox sandbox = new JexlSandbox(false, true); + sandbox.permissions(I.class.getName(), true, true, true, false); + System.out.println("permission B=" + sandbox.get(B.class.getName()).write()); + System.out.println("permission A=" + sandbox.get(A.class.getName()).write()); + } + + @Test + public void testIssue424() { + JexlSandbox sandbox = new JexlSandbox(false, true); + sandbox.permissions(Map.class.getName(), true, true, true, true); + String jexlCode = "x.foo = 'bar'"; + JexlEngine engine = new JexlBuilder() + .sandbox(sandbox) + .safe(false) + .strict(true).create(); + JexlContext context = new MapContext(); + Map<String, Object> x = new LinkedHashMap<>(); + context.set("x", x); + Object result = engine.createScript(jexlCode).execute(context); + assertEquals("bar", result); + assertEquals("bar", x.get("foo")); + } + }