diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index 7fcb9ce3b1..62dc00ca97 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -85,7 +85,7 @@ commons-lang3 - org.mvel + org.thingsboard mvel2 diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/DefaultMvelInvokeService.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/DefaultMvelInvokeService.java index 06f225cb1d..706ef2ed46 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/DefaultMvelInvokeService.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/DefaultMvelInvokeService.java @@ -22,15 +22,15 @@ import com.google.common.util.concurrent.MoreExecutors; import lombok.Getter; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.mvel2.ExecutionContext; import org.mvel2.MVEL; import org.mvel2.ParserContext; -import org.mvel2.optimizers.AccessorOptimizer; +import org.mvel2.SandboxedParserContext; import org.mvel2.optimizers.OptimizerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; -import org.springframework.util.ReflectionUtils; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.script.api.AbstractScriptInvokeService; import org.thingsboard.script.api.ScriptType; @@ -101,12 +101,9 @@ public class DefaultMvelInvokeService extends AbstractScriptInvokeService implem @PostConstruct public void init() { super.init(); - Field field = ReflectionUtils.findField(OptimizerFactory.class, "accessorCompilers"); - ReflectionUtils.makeAccessible(field); - Map accessorCompilers = (Map) field.get(null); - accessorCompilers.put(OptimizerFactory.SAFE_REFLECTIVE, new TbReflectiveAccessorOptimizer()); - - parserContext = new ParserContext(new TbMvelParserConfiguration()); + OptimizerFactory.setDefaultOptimizer(OptimizerFactory.SAFE_REFLECTIVE); + parserContext = new SandboxedParserContext(); + parserContext.addImport("JSON", TbJson.class); executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(2, "mvel-executor")); } @@ -159,7 +156,9 @@ public class DefaultMvelInvokeService extends AbstractScriptInvokeService implem throw new TbScriptException(scriptId, TbScriptException.ErrorCode.OTHER, null, new RuntimeException("Script not found!")); } try { - return MVEL.executeExpression(script.getCompiledScript(), script.createVars(args)); + ExecutionContext executionContext = new ExecutionContext(); + // TODO: + return MVEL.executeExpression(script.getCompiledScript(), executionContext, script.createVars(args)); } catch (OutOfMemoryError e) { Runtime.getRuntime().gc(); throw new TbScriptException(scriptId, TbScriptException.ErrorCode.OTHER, script.getScriptBody(), new RuntimeException("Memory error!")); diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbJson.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbJson.java index c9baba5322..18f3abb4df 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbJson.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbJson.java @@ -16,6 +16,8 @@ package org.thingsboard.script.api.mvel; import com.fasterxml.jackson.databind.JsonNode; +import org.mvel2.ExecutionContext; +import org.mvel2.util.ArgsRepackUtil; import org.thingsboard.common.util.JacksonUtil; import java.io.IOException; @@ -28,13 +30,13 @@ public class TbJson { return value != null ? JacksonUtil.toString(value) : "null"; } - public static Object parse(String value) throws IOException { + public static Object parse(ExecutionContext ctx, String value) throws IOException { if (value != null) { JsonNode node = JacksonUtil.toJsonNode(value); if (node.isObject()) { - return JacksonUtil.convertValue(node, Map.class); + return ArgsRepackUtil.repack(ctx, JacksonUtil.convertValue(node, Map.class)); } else if (node.isArray()) { - return JacksonUtil.convertValue(node, List.class); + return ArgsRepackUtil.repack(ctx, JacksonUtil.convertValue(node, List.class)); } else if (node.isDouble()) { return node.doubleValue(); } else if (node.isLong()) { diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbMapCreator.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbMapCreator.java deleted file mode 100644 index 7b00cbbc4f..0000000000 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbMapCreator.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright © 2016-2022 The Thingsboard Authors - * - * Licensed 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.thingsboard.script.api.mvel; - -import org.mvel2.compiler.Accessor; -import org.mvel2.compiler.ExecutableAccessor; -import org.mvel2.compiler.ExecutableStatement; -import org.mvel2.integration.VariableResolverFactory; -import org.mvel2.optimizers.impl.refl.collection.ExprValueAccessor; - -import java.util.HashMap; -import java.util.Map; - -public class TbMapCreator implements Accessor { - private Accessor[] keys; - private Accessor[] vals; - private int size; - - public Object getValue(Object ctx, Object elCtx, VariableResolverFactory variableFactory) { - Map map = new HashMap<>(size * 2); - for (int i = size - 1; i != -1; i--) { - //noinspection unchecked - map.put(getKey(i, ctx, elCtx, variableFactory), vals[i].getValue(ctx, elCtx, variableFactory)); - } - return map; - } - - private Object getKey(int index, Object ctx, Object elCtx, VariableResolverFactory variableFactory) { - Accessor keyAccessor = keys[index]; - if (keyAccessor instanceof ExprValueAccessor) { - ExecutableStatement executableStatement = ((ExprValueAccessor) keyAccessor).stmt; - if (executableStatement instanceof ExecutableAccessor) { - return ((ExecutableAccessor) executableStatement).getNode().getName(); - } - } - return keys[index].getValue(ctx, elCtx, variableFactory); - } - - public TbMapCreator(Accessor[] keys, Accessor[] vals) { - this.size = (this.keys = keys).length; - this.vals = vals; - } - - public Object setValue(Object ctx, Object elCtx, VariableResolverFactory variableFactory, Object value) { - // not implemented - return null; - } - - public Class getKnownEgressType() { - return Map.class; - } -} diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbMvelClassLoader.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbMvelClassLoader.java deleted file mode 100644 index 40f3bf70e2..0000000000 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbMvelClassLoader.java +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright © 2016-2022 The Thingsboard Authors - * - * Licensed 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.thingsboard.script.api.mvel; - -import org.mvel2.compiler.AbstractParser; - -import java.net.URL; -import java.net.URLClassLoader; -import java.util.HashSet; -import java.util.Set; - -public class TbMvelClassLoader extends URLClassLoader { - - private static final Set allowedClasses = new HashSet<>(); - private static final Set allowedPackages = new HashSet<>(); - - static { - - AbstractParser.LITERALS.remove("System"); - AbstractParser.LITERALS.remove("Runtime"); - AbstractParser.LITERALS.remove("Class"); - AbstractParser.LITERALS.remove("ClassLoader"); - AbstractParser.LITERALS.remove("Thread"); - AbstractParser.LITERALS.remove("Compiler"); - AbstractParser.LITERALS.remove("ThreadLocal"); - AbstractParser.LITERALS.remove("SecurityManager"); - - AbstractParser.CLASS_LITERALS.remove("System"); - AbstractParser.CLASS_LITERALS.remove("Runtime"); - AbstractParser.CLASS_LITERALS.remove("Class"); - AbstractParser.CLASS_LITERALS.remove("ClassLoader"); - AbstractParser.CLASS_LITERALS.remove("Thread"); - AbstractParser.CLASS_LITERALS.remove("Compiler"); - AbstractParser.CLASS_LITERALS.remove("ThreadLocal"); - AbstractParser.CLASS_LITERALS.remove("SecurityManager"); - AbstractParser.CLASS_LITERALS.put("JSON", TbJson.class); - AbstractParser.LITERALS.put("JSON", TbJson.class); - - AbstractParser.CLASS_LITERALS.values().forEach(val -> allowedClasses.add(((Class) val).getName())); - } - - static { - allowedPackages.add("org.mvel2"); - allowedPackages.add("java.util"); - } - - public TbMvelClassLoader() { - super(new URL[0], Thread.currentThread().getContextClassLoader()); - } - - @Override - protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - if (!classNameAllowed(name)) { - throw new ClassNotFoundException(); - } - return super.loadClass(name, resolve); - } - - @Override - public Class loadClass(String name) throws ClassNotFoundException { - if (!classNameAllowed(name)) { - throw new ClassNotFoundException(); - } - return super.loadClass(name); - } - - private boolean classNameAllowed(String name) { - if (allowedClasses.contains(name)) { - return true; - } - for (String pkgName : allowedPackages) { - if (name.startsWith(pkgName)) { - return true; - } - } - return false; - } - -} diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbMvelParserConfiguration.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbMvelParserConfiguration.java deleted file mode 100644 index 03f6e9bea0..0000000000 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbMvelParserConfiguration.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright © 2016-2022 The Thingsboard Authors - * - * Licensed 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.thingsboard.script.api.mvel; - -import org.mvel2.ParserConfiguration; -import org.mvel2.integration.VariableResolverFactory; - -public class TbMvelParserConfiguration extends ParserConfiguration { - - private static final long serialVersionUID = 5558151976348875590L; - - TbMvelParserConfiguration() { - setClassLoader(new TbMvelClassLoader()); - } - - @Override - public VariableResolverFactory getVariableFactory(VariableResolverFactory factory) { - if (Thread.interrupted()) { - throw new RuntimeException("Thread is interrupted!"); - } - return new TbMvelResolverFactory(factory); - } - -} diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbMvelResolverFactory.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbMvelResolverFactory.java deleted file mode 100644 index 2715057b3a..0000000000 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbMvelResolverFactory.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright © 2016-2022 The Thingsboard Authors - * - * Licensed 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.thingsboard.script.api.mvel; - -import org.mvel2.integration.VariableResolver; -import org.mvel2.integration.VariableResolverFactory; -import org.mvel2.integration.impl.StackResetResolverFactory; - -public class TbMvelResolverFactory extends StackResetResolverFactory { - - public TbMvelResolverFactory(VariableResolverFactory delegate) { - super(delegate); - } - - @Override - public VariableResolver getVariableResolver(String name) { - if (Thread.interrupted()) { - throw new RuntimeException("Thread is interrupted!"); - } - return super.getVariableResolver(name); - } -} diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbReflectiveAccessorOptimizer.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbReflectiveAccessorOptimizer.java deleted file mode 100644 index 0fb27a27fa..0000000000 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbReflectiveAccessorOptimizer.java +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright © 2016-2022 The Thingsboard Authors - * - * Licensed 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.thingsboard.script.api.mvel; - -import org.mvel2.ParserContext; -import org.mvel2.compiler.Accessor; -import org.mvel2.integration.VariableResolverFactory; -import org.mvel2.optimizers.impl.refl.ReflectiveAccessorOptimizer; -import org.mvel2.optimizers.impl.refl.collection.ArrayCreator; -import org.mvel2.optimizers.impl.refl.collection.ExprValueAccessor; -import org.mvel2.optimizers.impl.refl.collection.ListCreator; -import org.mvel2.optimizers.impl.refl.nodes.Union; - -import java.util.List; -import java.util.Map; - -import static org.mvel2.util.CompilerTools.expectType; -import static org.mvel2.util.ParseTools.findClass; -import static org.mvel2.util.ParseTools.getBaseComponentType; -import static org.mvel2.util.ParseTools.getSubComponentType; -import static org.mvel2.util.ParseTools.repeatChar; - -public class TbReflectiveAccessorOptimizer extends ReflectiveAccessorOptimizer { - private Object ctx; - private Class returnType; - private VariableResolverFactory variableFactory; - - @Override - public Accessor optimizeCollection(ParserContext pCtx, Object o, Class type, char[] property, int start, int offset, - Object ctx, Object thisRef, VariableResolverFactory factory) { - this.start = this.cursor = start; - this.length = start + offset; - this.returnType = type; - this.ctx = ctx; - this.variableFactory = factory; - this.pCtx = pCtx; - - Accessor root = _getAccessor(o, returnType); - - if (property != null && length > start) { - return new Union(pCtx, root, property, cursor, offset); - } else { - return root; - } - } - - private Accessor _getAccessor(Object o, Class type) { - if (o instanceof List) { - Accessor[] a = new Accessor[((List) o).size()]; - int i = 0; - - for (Object item : (List) o) { - a[i++] = _getAccessor(item, type); - } - - returnType = List.class; - - return new ListCreator(a); - } else if (o instanceof Map) { - Accessor[] k = new Accessor[((Map) o).size()]; - Accessor[] v = new Accessor[k.length]; - int i = 0; - - for (Object item : ((Map) o).keySet()) { - k[i] = _getAccessor(item, type); // key - v[i++] = _getAccessor(((Map) o).get(item), type); // value - } - - returnType = Map.class; - - return new TbMapCreator(k, v); - } else if (o instanceof Object[]) { - Accessor[] a = new Accessor[((Object[]) o).length]; - int i = 0; - int dim = 0; - - if (type != null) { - String nm = type.getName(); - while (nm.charAt(dim) == '[') dim++; - } else { - type = Object[].class; - dim = 1; - } - - try { - Class base = getBaseComponentType(type); - Class cls = dim > 1 ? findClass(null, repeatChar('[', dim - 1) + "L" + base.getName() + ";", pCtx) - : type; - - for (Object item : (Object[]) o) { - expectType(pCtx, a[i++] = _getAccessor(item, cls), base, true); - } - - return new ArrayCreator(a, getSubComponentType(type)); - } catch (ClassNotFoundException e) { - throw new RuntimeException("this error should never throw:" + getBaseComponentType(type).getName(), e); - } - } else { - if (returnType == null) returnType = Object.class; - if (type.isArray()) { - return new ExprValueAccessor((String) o, type, ctx, variableFactory, pCtx); - } else { - return new ExprValueAccessor((String) o, Object.class, ctx, variableFactory, pCtx); - } - } - } -} diff --git a/pom.xml b/pom.xml index c56316f44c..9589570026 100755 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ 3.5.5 3.17.2 1.42.1 - 2.4.14.Final + 2.4.16TB 1.18.18 1.2.4 4.1.75.Final @@ -1575,7 +1575,7 @@ ${grpc.version} - org.mvel + org.thingsboard mvel2 ${mvel.version}