From c52ece7c73599e0394e743e128bb55fe40cfce67 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 28 Sep 2023 09:43:31 +0200 Subject: [PATCH 1/8] [feature] set up allowed class-permissions to JEXL constructor, to use custom classes in scripts --- .../scxml2/env/jexl/JexlEvaluator.java | 18 ++++++++++++++++- src/test/java/com/custom/Payload.java | 20 +++++++++++++++++++ .../scxml2/env/jexl/JexlEvaluatorTest.java | 14 +++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/custom/Payload.java diff --git a/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java b/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java index 643e37360..bbb435af6 100644 --- a/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java +++ b/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java @@ -74,11 +74,21 @@ public Evaluator getEvaluator(final SCXML document) { /** The internal JexlEngine instance to use. */ private transient volatile JexlEngine jexlEngine; + /** Optional: saves user defined packages, which JEXL should allow for evaluation */ + private String[] customAllowedClasses; + /** Constructor. */ public JexlEvaluator() { jexlEngine = getJexlEngine(); } + /** Constructor with further allowed classes or packages. Use an asterix for all classes in a package */ + public JexlEvaluator(String... customAllowedClasses) { + this.customAllowedClasses = customAllowedClasses; + jexlEngine = getJexlEngine(); + } + + @Override public String getSupportedDatamodel() { return SUPPORTED_DATA_MODEL; @@ -176,7 +186,7 @@ public Context newContext(final Context parent) { /** * Create the internal JexlEngine member during the initialization. - * This method can be overriden to specify more detailed options + * This method can be overridden to specify more detailed options * into the JexlEngine. * @return new JexlEngine instance */ @@ -185,7 +195,13 @@ protected JexlEngine createJexlEngine() { // See javadoc of org.apache.commons.jexl2.JexlEngine#setFunctions(Map funcs) for detail. final Map funcs = new HashMap<>(); funcs.put(null, JexlBuiltin.class); + JexlPermissions permissions = JexlPermissions.RESTRICTED.compose("org.apache.commons.scxml2.*"); + + if(customAllowedClasses != null && customAllowedClasses.length > 0) { + permissions = permissions.compose(customAllowedClasses); + } + return new JexlBuilder().permissions(permissions).namespaces(funcs).cache(256).create(); } diff --git a/src/test/java/com/custom/Payload.java b/src/test/java/com/custom/Payload.java new file mode 100644 index 000000000..1bb7cbade --- /dev/null +++ b/src/test/java/com/custom/Payload.java @@ -0,0 +1,20 @@ +package com.custom; + +public class Payload { + + private final int id; + private final String someString; + + public Payload(int id, String someString) { + this.id = id; + this.someString = someString; + } + + public int getId() { + return id; + } + + public String getSomeString() { + return someString; + } +} diff --git a/src/test/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorTest.java b/src/test/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorTest.java index 2c0c01495..ebd04cc20 100644 --- a/src/test/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorTest.java +++ b/src/test/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorTest.java @@ -16,6 +16,7 @@ */ package org.apache.commons.scxml2.env.jexl; +import com.custom.Payload; import org.apache.commons.scxml2.Context; import org.apache.commons.scxml2.Evaluator; import org.apache.commons.scxml2.SCXMLExpressionException; @@ -60,4 +61,17 @@ public void testErrorMessage() { "JexlEvaluator: Incorrect error message"); } + @Test + void testEvalInCustomClass() throws SCXMLExpressionException { + + // Arrange + final Evaluator eval = new JexlEvaluator("com.custom.*"); + ctx.set("payload", new Payload(1, "hello")); + + // Act + final Object result = eval.evalScript(ctx, "payload.getId()"); + + // Assert + Assertions.assertEquals(1, result); + } } From 47336f2a69e3427ec2e0ba75dc37fefadde68457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20M=C3=BCller?= Date: Thu, 28 Sep 2023 18:46:38 +0200 Subject: [PATCH 2/8] Adds license --- src/test/java/com/custom/Payload.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/test/java/com/custom/Payload.java b/src/test/java/com/custom/Payload.java index 1bb7cbade..d32ca832f 100644 --- a/src/test/java/com/custom/Payload.java +++ b/src/test/java/com/custom/Payload.java @@ -1,3 +1,19 @@ +/* + * 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 com.custom; public class Payload { From 20756539b8a0d46be5d64497592b3bc2a28c5586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20M=C3=BCller?= Date: Thu, 28 Sep 2023 18:46:46 +0200 Subject: [PATCH 3/8] Fixes formatting --- .../java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java b/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java index bbb435af6..327eafbb4 100644 --- a/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java +++ b/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java @@ -198,7 +198,7 @@ protected JexlEngine createJexlEngine() { JexlPermissions permissions = JexlPermissions.RESTRICTED.compose("org.apache.commons.scxml2.*"); - if(customAllowedClasses != null && customAllowedClasses.length > 0) { + if (customAllowedClasses != null && customAllowedClasses.length > 0) { permissions = permissions.compose(customAllowedClasses); } From cea549943a9495bcbdce60ef114b526cdaf6bdde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20M=C3=BCller?= Date: Sat, 7 Oct 2023 22:16:24 +0200 Subject: [PATCH 4/8] Adds a test for a custom JexlEvaluator created by JexlEvaluatorBuilder --- .../commons/scxml2/SCXMLExecutorTest.java | 17 ++++++++ .../commons/scxml2/external-payload.xml | 40 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 src/test/java/org/apache/commons/scxml2/external-payload.xml diff --git a/src/test/java/org/apache/commons/scxml2/SCXMLExecutorTest.java b/src/test/java/org/apache/commons/scxml2/SCXMLExecutorTest.java index 90b2cf6cf..af14aebc7 100644 --- a/src/test/java/org/apache/commons/scxml2/SCXMLExecutorTest.java +++ b/src/test/java/org/apache/commons/scxml2/SCXMLExecutorTest.java @@ -21,6 +21,9 @@ import java.util.Map; import java.util.Set; +import com.custom.Payload; +import org.apache.commons.scxml2.env.jexl.JexlEvaluator; +import org.apache.commons.scxml2.env.jexl.JexlEvaluatorBuilder; import org.apache.commons.scxml2.model.EnterableState; import org.apache.commons.scxml2.model.TransitionTarget; import org.junit.jupiter.api.Assertions; @@ -289,6 +292,20 @@ public void testSCXMLExecutorFinalDoneData() throws Exception { Assertions.assertEquals("done", exec.getFinalDoneData()); } + @Test + public void testSCXMLExecutorWithExternalPayloadObject() throws Exception { + final SCXMLExecutor exec = SCXMLTestHelper.getExecutor("org/apache/commons/scxml2/external-payload.xml"); + final JexlEvaluator evaluator = new JexlEvaluatorBuilder() + .addAllowedPackage("com.custom") + .build(); + + exec.setEvaluator(evaluator); + + exec.go(); + SCXMLTestHelper.assertPostTriggerState(exec, "done", new Payload(1, "someString"), "end"); + Assertions.assertEquals(1, exec.getGlobalContext().getVars().get("idFromEventPayload")); + } + private void checkMicrowave01Sample(final SCXMLExecutor exec) throws Exception { final Set currentStates = SCXMLTestHelper.fireEvent(exec, "turn_on"); Assertions.assertEquals(1, currentStates.size()); diff --git a/src/test/java/org/apache/commons/scxml2/external-payload.xml b/src/test/java/org/apache/commons/scxml2/external-payload.xml new file mode 100644 index 000000000..47cd63dd7 --- /dev/null +++ b/src/test/java/org/apache/commons/scxml2/external-payload.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + From 92bfb091dbf760ef74cbfa437c12283d56b0c652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20M=C3=BCller?= Date: Sat, 7 Oct 2023 22:16:44 +0200 Subject: [PATCH 5/8] Contributor added --- pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pom.xml b/pom.xml index 644361412..9995c4e04 100644 --- a/pom.xml +++ b/pom.xml @@ -134,6 +134,9 @@ Ales Dolecek + + Nick Müller + From 7332e424f04c094943be61b8055ecfd54264c15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20M=C3=BCller?= Date: Sat, 7 Oct 2023 22:17:07 +0200 Subject: [PATCH 6/8] Renaming variable --- .../commons/scxml2/env/jexl/JexlEvaluator.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java b/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java index 327eafbb4..ae52cdef4 100644 --- a/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java +++ b/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java @@ -74,8 +74,8 @@ public Evaluator getEvaluator(final SCXML document) { /** The internal JexlEngine instance to use. */ private transient volatile JexlEngine jexlEngine; - /** Optional: saves user defined packages, which JEXL should allow for evaluation */ - private String[] customAllowedClasses; + /** Optional: saves user defined packages, which JEXL should allow or deny for evaluation */ + private String[] customJexlPermissions; /** Constructor. */ public JexlEvaluator() { @@ -83,8 +83,8 @@ public JexlEvaluator() { } /** Constructor with further allowed classes or packages. Use an asterix for all classes in a package */ - public JexlEvaluator(String... customAllowedClasses) { - this.customAllowedClasses = customAllowedClasses; + public JexlEvaluator(JexlEvaluatorBuilder builder) { + customJexlPermissions = builder.getJexlPermissions(); jexlEngine = getJexlEngine(); } @@ -198,8 +198,8 @@ protected JexlEngine createJexlEngine() { JexlPermissions permissions = JexlPermissions.RESTRICTED.compose("org.apache.commons.scxml2.*"); - if (customAllowedClasses != null && customAllowedClasses.length > 0) { - permissions = permissions.compose(customAllowedClasses); + if (customJexlPermissions != null && customJexlPermissions.length > 0) { + permissions = permissions.compose(customJexlPermissions); } return new JexlBuilder().permissions(permissions).namespaces(funcs).cache(256).create(); From 23d4f3b853b52cc5c9ba5fc76f9682b58c513047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20M=C3=BCller?= Date: Sat, 7 Oct 2023 22:17:29 +0200 Subject: [PATCH 7/8] Builder for JexlEvaluator added --- .../scxml2/env/jexl/JexlEvaluatorBuilder.java | 74 +++++++++++++++++++ .../env/jexl/JexlEvaluatorBuilderTest.java | 35 +++++++++ .../scxml2/env/jexl/JexlEvaluatorTest.java | 5 +- 3 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorBuilder.java create mode 100644 src/test/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorBuilderTest.java diff --git a/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorBuilder.java b/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorBuilder.java new file mode 100644 index 000000000..dd62f87bb --- /dev/null +++ b/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorBuilder.java @@ -0,0 +1,74 @@ +/* + * 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.scxml2.env.jexl; + +import java.util.ArrayList; +import java.util.List; + +public class JexlEvaluatorBuilder { + + private final List allowedPackages = new ArrayList<>(); + private final List deniedClasses = new ArrayList<>(); + + /** + * Adds a whole package to the JEXL permissions and allows to use this classes in JEXL expressions. + * + * @param fullQualifiedPackageName expects the complete path f.e. "org.apache.commons.logging" + */ + public JexlEvaluatorBuilder addAllowedPackage(String fullQualifiedPackageName) { + allowedPackages.add(fullQualifiedPackageName); + return this; + } + + /** + * Adds a specific class, which is not allowed to be used in JEXL expressions. + * + * @param fullQualifiedClassName expects a complete path f.e. "org.apache.commons.logging.Logger" + */ + public JexlEvaluatorBuilder addDeniedClass(String fullQualifiedClassName) { + deniedClasses.add(fullQualifiedClassName); + return this; + } + + /** + * Creates a JexlEvaluator by the defined options. + */ + public JexlEvaluator build() { + return new JexlEvaluator(this); + } + + /** + * Package-private: converts the user given information to JEXL understandable src-list + */ + String[] getJexlPermissions() { + List permissions = new ArrayList<>(); + + for (String allowedPackage : allowedPackages) { + permissions.add(allowedPackage.replace(".*", "") + ".*"); + } + + for (String deniedClass : deniedClasses) { + final int lastDot = deniedClass.lastIndexOf('.'); + String packageName = deniedClass.substring(0, lastDot); + String className = deniedClass.substring(lastDot + 1); + permissions.add(packageName + " { " + className + " {} }"); + } + + return permissions.toArray(new String[]{}); + } +} + diff --git a/src/test/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorBuilderTest.java b/src/test/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorBuilderTest.java new file mode 100644 index 000000000..514c031dc --- /dev/null +++ b/src/test/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorBuilderTest.java @@ -0,0 +1,35 @@ +package org.apache.commons.scxml2.env.jexl; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class JexlEvaluatorBuilderTest { + + @Test + public void testAddAllowedPackages() { + + final String[] jexlPermissions = new JexlEvaluatorBuilder() + .addAllowedPackage("org.apache.commons.scxml2") + .addAllowedPackage("org.apache.commons.logging") + .getJexlPermissions(); + + Assertions.assertEquals(2, jexlPermissions.length); + Assertions.assertEquals("org.apache.commons.scxml2.*", jexlPermissions[0]); + Assertions.assertEquals("org.apache.commons.logging.*", jexlPermissions[1]); + } + + @Test + public void testAllowedPackagesAndDeniedClasses() { + + final String[] jexlPermissions = new JexlEvaluatorBuilder() + .addAllowedPackage("org.apache.commons.scxml2") + .addDeniedClass("org.apache.commons.scxml2.Builtin") + .addDeniedClass("org.apache.commons.logging.Logger") + .getJexlPermissions(); + + Assertions.assertEquals(3, jexlPermissions.length); + Assertions.assertEquals("org.apache.commons.scxml2.*", jexlPermissions[0]); + Assertions.assertEquals("org.apache.commons.scxml2 { Builtin {} }", jexlPermissions[1]); + Assertions.assertEquals("org.apache.commons.logging { Logger {} }", jexlPermissions[2]); + } +} \ No newline at end of file diff --git a/src/test/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorTest.java b/src/test/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorTest.java index ebd04cc20..8b462b805 100644 --- a/src/test/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorTest.java +++ b/src/test/java/org/apache/commons/scxml2/env/jexl/JexlEvaluatorTest.java @@ -65,7 +65,10 @@ public void testErrorMessage() { void testEvalInCustomClass() throws SCXMLExpressionException { // Arrange - final Evaluator eval = new JexlEvaluator("com.custom.*"); + final JexlEvaluator eval = new JexlEvaluatorBuilder() + .addAllowedPackage("com.custom") + .build(); + ctx.set("payload", new Payload(1, "hello")); // Act From 90c19b3b4ae98683f616e60311af2debc721fd75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20M=C3=BCller?= Date: Sun, 8 Oct 2023 11:12:25 +0200 Subject: [PATCH 8/8] JavaDoc updated --- .../java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java b/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java index ae52cdef4..75f08de51 100644 --- a/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java +++ b/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java @@ -82,7 +82,7 @@ public JexlEvaluator() { jexlEngine = getJexlEngine(); } - /** Constructor with further allowed classes or packages. Use an asterix for all classes in a package */ + /** Constructor with a given builder to set up specific options */ public JexlEvaluator(JexlEvaluatorBuilder builder) { customJexlPermissions = builder.getJexlPermissions(); jexlEngine = getJexlEngine();