This is an automated email from the ASF dual-hosted git repository. SvenO3 pushed a commit to branch remove-groovy-support-from-adapter-script-transformation in repository https://gitbox.apache.org/repos/asf/streampipes.git
commit 062d7ba5210d3ab985366b0e5f0566b4503baecc Author: Sven Oehler <[email protected]> AuthorDate: Mon Jun 29 13:25:30 2026 +0200 Delete groovy module --- streampipes-connect-transformer-groovy/pom.xml | 78 ----- .../transformer/groovy/GroovyScriptContext.java | 25 -- .../transformer/groovy/GroovyScriptEngine.java | 80 ------ .../sandbox/RestrictedGroovyClassLoader.java | 59 ---- .../transformer/groovy/sandbox/SandboxPolicy.java | 187 ------------ .../transformer/groovy/sandbox/ScriptSandbox.java | 51 ---- .../ast/AstChecksCompilationCustomizer.java | 39 --- .../groovy/sandbox/ast/AstChecksVisitor.java | 109 ------- .../sandbox/ast/SecureCustomizerFactory.java | 59 ---- .../error/SandboxCompilationMessageResolver.java | 112 -------- .../SandboxViolationClassNotFoundException.java | 26 -- .../sandbox/error/SandboxViolationException.java | 26 -- .../transformer/groovy/GroovyScriptEngineTest.java | 318 --------------------- 13 files changed, 1169 deletions(-) diff --git a/streampipes-connect-transformer-groovy/pom.xml b/streampipes-connect-transformer-groovy/pom.xml deleted file mode 100644 index e634d028da..0000000000 --- a/streampipes-connect-transformer-groovy/pom.xml +++ /dev/null @@ -1,78 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - ~ 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. - ~ - --> - - -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <groupId>org.apache.streampipes</groupId> - <artifactId>streampipes-parent</artifactId> - <version>0.99.0-SNAPSHOT</version> - </parent> - - <artifactId>streampipes-connect-transformer-groovy</artifactId> - - <properties> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - </properties> - - <dependencies> - <dependency> - <groupId>org.apache.streampipes</groupId> - <artifactId>streampipes-connect-transformer-api</artifactId> - <version>0.99.0-SNAPSHOT</version> - </dependency> - <dependency> - <groupId>org.apache.streampipes</groupId> - <artifactId>streampipes-client-api</artifactId> - <version>0.99.0-SNAPSHOT</version> - </dependency> - <dependency> - <groupId>org.apache.groovy</groupId> - <artifactId>groovy</artifactId> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-api</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.apache.streampipes</groupId> - <artifactId>streampipes-client</artifactId> - <version>0.99.0-SNAPSHOT</version> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - <configuration> - <propertyExpansion> - checkstyle.config.base.path=${project.parent.basedir}/tools/maven - </propertyExpansion> - </configuration> - </plugin> - </plugins> - </build> -</project> diff --git a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/GroovyScriptContext.java b/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/GroovyScriptContext.java deleted file mode 100644 index 4d5f0029e7..0000000000 --- a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/GroovyScriptContext.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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.streampipes.connect.transformer.groovy; - -import org.apache.streampipes.client.api.IStreamPipesClient; -import org.apache.streampipes.connect.transformer.api.Context; - -public record GroovyScriptContext(IStreamPipesClient client) implements Context { -} diff --git a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/GroovyScriptEngine.java b/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/GroovyScriptEngine.java deleted file mode 100644 index 1baf7f59d4..0000000000 --- a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/GroovyScriptEngine.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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.streampipes.connect.transformer.groovy; - -import org.apache.streampipes.connect.transformer.api.Context; -import org.apache.streampipes.connect.transformer.api.OutputCollector; -import org.apache.streampipes.connect.transformer.api.ScriptTransformer; -import org.apache.streampipes.connect.transformer.api.TransformationEngine; -import org.apache.streampipes.connect.transformer.api.exception.ScriptCompilationException; -import org.apache.streampipes.connect.transformer.api.exception.ScriptExecutionException; -import org.apache.streampipes.connect.transformer.api.utils.TransformationEngineConversionUtils; -import org.apache.streampipes.connect.transformer.groovy.sandbox.ScriptSandbox; -import org.apache.streampipes.model.connect.ScriptMetadata; - -import groovy.lang.Binding; -import groovy.lang.GroovyShell; -import groovy.lang.Script; -import org.codehaus.groovy.runtime.InvokerHelper; - -import java.util.Map; - -public class GroovyScriptEngine implements TransformationEngine { - - @Override - public ScriptMetadata metadata() { - return new ScriptMetadata( - "groovy", - "Groovy", - "out.collect(input)" - ); - } - - @Override - public ScriptTransformer compile(String code) throws ScriptCompilationException { - GroovyShell shell = ScriptSandbox.createShell(GroovyScriptEngine.class.getClassLoader()); - Script script; - try { - script = shell.parse(code); - } catch (Exception e) { - throw new ScriptCompilationException(ScriptSandbox.resolveCompilationErrorMessage(e), e); - } - - Class<? extends Script> scriptClass = script.getClass(); - - return (input, out, ctx) -> execute(scriptClass, input, out, ctx); - } - - private void execute(Class<? extends Script> scriptClass, - Map<String, Object> input, - OutputCollector<Map<String, Object>> out, - Context ctx) - throws ScriptExecutionException { - try { - Binding binding = new Binding(); - binding.setVariable("input", input); - binding.setVariable("out", TransformationEngineConversionUtils.convertingCollector(out, metadata().language())); - binding.setVariable("ctx", ctx); - Script scriptInstance = InvokerHelper.createScript(scriptClass, binding); - scriptInstance.run(); - } catch (Exception e) { - throw new ScriptExecutionException("Groovy template execution failed", e); - } - } -} diff --git a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/RestrictedGroovyClassLoader.java b/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/RestrictedGroovyClassLoader.java deleted file mode 100644 index adb3e28fca..0000000000 --- a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/RestrictedGroovyClassLoader.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.streampipes.connect.transformer.groovy.sandbox; - -import org.apache.streampipes.connect.transformer.groovy.sandbox.error.SandboxViolationClassNotFoundException; - -import groovy.lang.GroovyClassLoader; -import org.codehaus.groovy.control.CompilationFailedException; -import org.codehaus.groovy.control.CompilerConfiguration; - -public final class RestrictedGroovyClassLoader extends GroovyClassLoader { - - public RestrictedGroovyClassLoader(ClassLoader parent, CompilerConfiguration configuration) { - super(parent, configuration); - } - - @Override - public Class<?> loadClass(String name) throws ClassNotFoundException { - rejectForbiddenClass(name); - return super.loadClass(name); - } - - @Override - protected Class<?> loadClass(String name, boolean resolve) - throws ClassNotFoundException, CompilationFailedException { - rejectForbiddenClass(name); - return super.loadClass(name, resolve); - } - - @Override - public Class loadClass(String name, boolean lookupScriptFiles, boolean preferClassOverScript, boolean resolve) - throws ClassNotFoundException, CompilationFailedException { - rejectForbiddenClass(name); - return super.loadClass(name, lookupScriptFiles, preferClassOverScript, resolve); - } - - private void rejectForbiddenClass(String className) throws ClassNotFoundException { - if (SandboxPolicy.blocksType(className)) { - throw new SandboxViolationClassNotFoundException( - SandboxPolicy.VIOLATION_MESSAGE + ": access to class '" + className + "' is forbidden"); - } - } -} diff --git a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/SandboxPolicy.java b/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/SandboxPolicy.java deleted file mode 100644 index 18b41e3b51..0000000000 --- a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/SandboxPolicy.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * 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.streampipes.connect.transformer.groovy.sandbox; - -import groovy.lang.Binding; - -import java.util.List; -import java.util.Set; - -public final class SandboxPolicy { - - public static final String VIOLATION_MESSAGE = "Groovy script violates sandbox restrictions"; - - private static final List<String> DISALLOWED_IMPORTS = List.of( - "java.io.File", - "java.io.FileInputStream", - "java.io.FileOutputStream", - "java.io.RandomAccessFile", - "java.net.InetAddress", - "java.net.ServerSocket", - "java.net.Socket", - "java.net.URI", - "java.net.URL", - "java.lang.Class", - "java.lang.ClassLoader", - "java.lang.Process", - "java.lang.ProcessBuilder", - "java.lang.Runtime", - "java.lang.System", - "java.lang.Thread", - "java.nio.channels.FileChannel", - "java.nio.file.Files", - "java.nio.file.Path", - "java.nio.file.Paths", - "groovy.lang.GroovyClassLoader", - "groovy.lang.GroovyShell", - "groovy.transform.ASTTest", - "groovy.util.Eval" - ); - private static final List<String> DISALLOWED_STAR_IMPORTS = List.of( - "java.io", - "java.net", - "java.nio", - "java.nio.channels", - "java.nio.file", - "java.lang.reflect", - "groovy.io" - ); - private static final List<String> DISALLOWED_STATIC_IMPORTS = List.of( - "java.lang.System", - "java.lang.Runtime", - "java.nio.file.Files", - "java.nio.file.Paths" - ); - private static final Set<String> DISABLED_GLOBAL_AST_TRANSFORMATIONS = Set.of( - "groovy.grape.GrabAnnotationTransformation", - "org.codehaus.groovy.transform.ASTTestTransformation", - "org.codehaus.groovy.transform.AnnotationCollectorTransform" - ); - private static final Set<String> DISALLOWED_EXACT_TYPES = Set.of( - "java.io.File", - "java.io.FileInputStream", - "java.io.FileOutputStream", - "java.io.RandomAccessFile", - "java.net.InetAddress", - "java.net.ServerSocket", - "java.net.Socket", - "java.net.URI", - "java.net.URL", - "java.lang.Class", - "java.lang.ClassLoader", - "java.lang.Process", - "java.lang.ProcessBuilder", - "java.lang.Runtime", - "java.lang.System", - "java.lang.Thread", - "java.nio.channels.FileChannel", - "java.nio.file.Files", - "java.nio.file.Path", - "java.nio.file.Paths", - "groovy.lang.GroovyClassLoader", - "groovy.lang.GroovyShell", - "groovy.transform.ASTTest", - "groovy.util.Eval" - ); - private static final Set<String> DISALLOWED_SIMPLE_TYPES = Set.of( - "File", - "FileInputStream", - "FileOutputStream", - "RandomAccessFile", - "InetAddress", - "ServerSocket", - "Socket", - "URI", - "URL", - "Class", - "ClassLoader", - "Process", - "ProcessBuilder", - "Runtime", - "System", - "Thread", - "FileChannel", - "Files", - "Path", - "Paths", - "GroovyClassLoader", - "GroovyShell", - "ASTTest", - "Eval" - ); - private static final List<String> DISALLOWED_TYPE_PREFIXES = List.of( - "java.io.", - "java.net.", - "java.nio.", - "java.nio.channels.", - "java.nio.file.", - "java.lang.reflect.", - "groovy.io." - ); - private static final Set<String> DISALLOWED_PROPERTY_NAMES = Set.of( - "class", - "classLoader", - "metaClass" - ); - - private SandboxPolicy() { - } - - public static Binding createBinding() { - return new Binding(); - } - - public static List<String> disallowedImports() { - return DISALLOWED_IMPORTS; - } - - public static List<String> disallowedStarImports() { - return DISALLOWED_STAR_IMPORTS; - } - - public static List<String> disallowedStaticImports() { - return DISALLOWED_STATIC_IMPORTS; - } - - public static Set<String> disabledGlobalAstTransformations() { - return DISABLED_GLOBAL_AST_TRANSFORMATIONS; - } - - public static Set<String> disallowedPropertyNames() { - return DISALLOWED_PROPERTY_NAMES; - } - - public static boolean blocksType(String typeName) { - if (typeName == null || typeName.isBlank()) { - return false; - } - - if (DISALLOWED_EXACT_TYPES.contains(typeName) || DISALLOWED_SIMPLE_TYPES.contains(typeName)) { - return true; - } - - for (String prefix : DISALLOWED_TYPE_PREFIXES) { - if (typeName.startsWith(prefix)) { - return true; - } - } - - return false; - } -} diff --git a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/ScriptSandbox.java b/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/ScriptSandbox.java deleted file mode 100644 index 2f26ec4365..0000000000 --- a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/ScriptSandbox.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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.streampipes.connect.transformer.groovy.sandbox; - -import org.apache.streampipes.connect.transformer.groovy.sandbox.ast.AstChecksCompilationCustomizer; -import org.apache.streampipes.connect.transformer.groovy.sandbox.ast.SecureCustomizerFactory; -import org.apache.streampipes.connect.transformer.groovy.sandbox.error.SandboxCompilationMessageResolver; - -import groovy.lang.GroovyShell; -import org.codehaus.groovy.control.CompilerConfiguration; - -public final class ScriptSandbox { - - private ScriptSandbox() { - } - - public static GroovyShell createShell(ClassLoader parent) { - CompilerConfiguration configuration = new CompilerConfiguration(); - configuration.setDisabledGlobalASTTransformations(SandboxPolicy.disabledGlobalAstTransformations()); - configuration.addCompilationCustomizers( - SecureCustomizerFactory.create(), - new AstChecksCompilationCustomizer() - ); - - return new GroovyShell( - new RestrictedGroovyClassLoader(parent, configuration), - SandboxPolicy.createBinding(), - configuration - ); - } - - public static String resolveCompilationErrorMessage(Exception error) { - return SandboxCompilationMessageResolver.resolve(error); - } -} diff --git a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/ast/AstChecksCompilationCustomizer.java b/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/ast/AstChecksCompilationCustomizer.java deleted file mode 100644 index 05a6a7751d..0000000000 --- a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/ast/AstChecksCompilationCustomizer.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.streampipes.connect.transformer.groovy.sandbox.ast; - -import org.codehaus.groovy.ast.ClassNode; -import org.codehaus.groovy.classgen.GeneratorContext; -import org.codehaus.groovy.control.CompilationFailedException; -import org.codehaus.groovy.control.CompilePhase; -import org.codehaus.groovy.control.SourceUnit; -import org.codehaus.groovy.control.customizers.CompilationCustomizer; - -public final class AstChecksCompilationCustomizer extends CompilationCustomizer { - - public AstChecksCompilationCustomizer() { - super(CompilePhase.SEMANTIC_ANALYSIS); - } - - @Override - public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) - throws CompilationFailedException { - new AstChecksVisitor(source).visitClass(classNode); - } -} diff --git a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/ast/AstChecksVisitor.java b/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/ast/AstChecksVisitor.java deleted file mode 100644 index 7fd75a2465..0000000000 --- a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/ast/AstChecksVisitor.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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.streampipes.connect.transformer.groovy.sandbox.ast; - -import org.apache.streampipes.connect.transformer.groovy.sandbox.SandboxPolicy; -import org.apache.streampipes.connect.transformer.groovy.sandbox.error.SandboxViolationException; - -import org.codehaus.groovy.ast.ClassCodeVisitorSupport; -import org.codehaus.groovy.ast.ClassNode; -import org.codehaus.groovy.ast.expr.ClassExpression; -import org.codehaus.groovy.ast.expr.ConstructorCallExpression; -import org.codehaus.groovy.ast.expr.Expression; -import org.codehaus.groovy.ast.expr.MethodCallExpression; -import org.codehaus.groovy.ast.expr.PropertyExpression; -import org.codehaus.groovy.ast.expr.StaticMethodCallExpression; -import org.codehaus.groovy.ast.expr.VariableExpression; -import org.codehaus.groovy.control.SourceUnit; - -public final class AstChecksVisitor extends ClassCodeVisitorSupport { - - private final SourceUnit sourceUnit; - - public AstChecksVisitor(SourceUnit sourceUnit) { - this.sourceUnit = sourceUnit; - } - - @Override - protected SourceUnit getSourceUnit() { - return sourceUnit; - } - - @Override - public void visitConstructorCallExpression(ConstructorCallExpression call) { - rejectForbiddenType(call.getType(), "constructor access"); - super.visitConstructorCallExpression(call); - } - - @Override - public void visitClassExpression(ClassExpression expression) { - rejectForbiddenType(expression.getType(), "class access"); - super.visitClassExpression(expression); - } - - @Override - public void visitMethodCallExpression(MethodCallExpression call) { - rejectForbiddenExpression(call.getObjectExpression(), "method access"); - super.visitMethodCallExpression(call); - } - - @Override - public void visitStaticMethodCallExpression(StaticMethodCallExpression call) { - rejectForbiddenType(call.getOwnerType(), "static method access"); - super.visitStaticMethodCallExpression(call); - } - - @Override - public void visitPropertyExpression(PropertyExpression expression) { - String propertyName = expression.getPropertyAsString(); - if (propertyName != null && SandboxPolicy.disallowedPropertyNames().contains(propertyName)) { - throw new SandboxViolationException( - SandboxPolicy.VIOLATION_MESSAGE + ": access to property '" + propertyName + "' is forbidden"); - } - - rejectForbiddenExpression(expression.getObjectExpression(), "property access"); - super.visitPropertyExpression(expression); - } - - private void rejectForbiddenExpression(Expression expression, String operation) { - if (expression instanceof ClassExpression classExpression) { - rejectForbiddenType(classExpression.getType(), operation); - } else if (expression instanceof VariableExpression variableExpression) { - if (SandboxPolicy.blocksType(variableExpression.getName())) { - throw new SandboxViolationException( - SandboxPolicy.VIOLATION_MESSAGE + ": access to class '" + variableExpression.getName() + "' is forbidden"); - } - } else { - rejectForbiddenType(expression.getType(), operation); - } - } - - private void rejectForbiddenType(ClassNode classNode, String operation) { - if (classNode != null) { - rejectForbiddenType(classNode.getName(), operation); - } - } - - private void rejectForbiddenType(String typeName, String operation) { - if (SandboxPolicy.blocksType(typeName)) { - throw new SandboxViolationException( - SandboxPolicy.VIOLATION_MESSAGE + ": " + operation + " to '" + typeName + "' is forbidden"); - } - } -} diff --git a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/ast/SecureCustomizerFactory.java b/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/ast/SecureCustomizerFactory.java deleted file mode 100644 index 4447a0ac02..0000000000 --- a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/ast/SecureCustomizerFactory.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.streampipes.connect.transformer.groovy.sandbox.ast; - -import org.apache.streampipes.connect.transformer.groovy.sandbox.SandboxPolicy; - -import org.codehaus.groovy.control.customizers.SecureASTCustomizer; - -import java.util.List; - -public final class SecureCustomizerFactory { - - private SecureCustomizerFactory() { - } - - public static SecureASTCustomizer create() { - SecureASTCustomizer customizer = new SecureASTCustomizer(); - customizer.setClosuresAllowed(true); - customizer.setMethodDefinitionAllowed(false); - customizer.setPackageAllowed(false); - customizer.setIndirectImportCheckEnabled(true); - customizer.setDisallowedImports(SandboxPolicy.disallowedImports()); - customizer.setDisallowedStarImports(SandboxPolicy.disallowedStarImports()); - customizer.setDisallowedStaticImports(SandboxPolicy.disallowedStaticImports()); - customizer.setDisallowedStaticStarImports(SandboxPolicy.disallowedStaticImports()); - customizer.setDisallowedReceiversClasses(List.of( - java.io.File.class, - java.lang.Class.class, - java.lang.ClassLoader.class, - java.lang.Process.class, - java.lang.ProcessBuilder.class, - java.lang.Runtime.class, - java.lang.System.class, - java.lang.Thread.class, - java.net.InetAddress.class, - java.net.ServerSocket.class, - java.net.Socket.class, - java.net.URI.class, - java.net.URL.class - )); - return customizer; - } -} diff --git a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/error/SandboxCompilationMessageResolver.java b/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/error/SandboxCompilationMessageResolver.java deleted file mode 100644 index 75de065cef..0000000000 --- a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/error/SandboxCompilationMessageResolver.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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.streampipes.connect.transformer.groovy.sandbox.error; - -import org.apache.streampipes.connect.transformer.groovy.sandbox.SandboxPolicy; - -import java.util.Optional; - -public final class SandboxCompilationMessageResolver { - - private SandboxCompilationMessageResolver() { - } - - public static String resolve(Exception error) { - Optional<SandboxViolationException> sandboxViolation = findSandboxViolation(error); - if (sandboxViolation.isPresent()) { - return sandboxViolation.get().getMessage(); - } - - return extractViolationMessage(error.getMessage()) - .or(() -> extractAllowedError(error.getMessage())) - .orElse("Failed to compile Groovy template"); - } - - private static Optional<SandboxViolationException> findSandboxViolation(Throwable error) { - Throwable current = error; - while (current != null) { - if (current instanceof SandboxViolationException sandboxViolation) { - return Optional.of(sandboxViolation); - } else if (current instanceof SandboxViolationClassNotFoundException sandboxViolation) { - return Optional.of(new SandboxViolationException(sandboxViolation.getMessage())); - } else if (current instanceof SecurityException securityException) { - return toSandboxViolation(securityException.getMessage()); - } - current = current.getCause(); - } - - return Optional.empty(); - } - - private static Optional<SandboxViolationException> toSandboxViolation(String message) { - if (message == null || message.isBlank()) { - return Optional.empty(); - } - - if (message.contains(SandboxPolicy.VIOLATION_MESSAGE)) { - return Optional.of(new SandboxViolationException(message)); - } - - if (message.contains("is not allowed")) { - return Optional.of(new SandboxViolationException(SandboxPolicy.VIOLATION_MESSAGE + ": " + message)); - } - - return Optional.empty(); - } - - private static Optional<String> extractViolationMessage(String message) { - if (message == null) { - return Optional.empty(); - } - - int messageIndex = message.indexOf(SandboxPolicy.VIOLATION_MESSAGE); - if (messageIndex < 0) { - return Optional.empty(); - } - - int lineBreakIndex = message.indexOf('\n', messageIndex); - if (lineBreakIndex < 0) { - return Optional.of(message.substring(messageIndex).trim()); - } - - return Optional.of(message.substring(messageIndex, lineBreakIndex).trim()); - } - - private static Optional<String> extractAllowedError(String message) { - if (message == null) { - return Optional.empty(); - } - - int messageIndex = message.indexOf("is not allowed"); - if (messageIndex < 0) { - return Optional.empty(); - } - - int lineStartIndex = message.lastIndexOf('\n', messageIndex); - int start = lineStartIndex < 0 ? 0 : lineStartIndex + 1; - int lineBreakIndex = message.indexOf('\n', messageIndex); - String extracted = (lineBreakIndex < 0 ? message.substring(start) : message.substring(start, lineBreakIndex)).trim(); - - if (extracted.isEmpty()) { - return Optional.empty(); - } - - return Optional.of(SandboxPolicy.VIOLATION_MESSAGE + ": " + extracted); - } -} diff --git a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/error/SandboxViolationClassNotFoundException.java b/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/error/SandboxViolationClassNotFoundException.java deleted file mode 100644 index af7cab2a3f..0000000000 --- a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/error/SandboxViolationClassNotFoundException.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.streampipes.connect.transformer.groovy.sandbox.error; - -public final class SandboxViolationClassNotFoundException extends ClassNotFoundException { - - public SandboxViolationClassNotFoundException(String message) { - super(message); - } -} diff --git a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/error/SandboxViolationException.java b/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/error/SandboxViolationException.java deleted file mode 100644 index 765e1a901e..0000000000 --- a/streampipes-connect-transformer-groovy/src/main/java/org/apache/streampipes/connect/transformer/groovy/sandbox/error/SandboxViolationException.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.streampipes.connect.transformer.groovy.sandbox.error; - -public final class SandboxViolationException extends SecurityException { - - public SandboxViolationException(String message) { - super(message); - } -} diff --git a/streampipes-connect-transformer-groovy/src/test/java/org/apache/streampipes/connect/transformer/groovy/GroovyScriptEngineTest.java b/streampipes-connect-transformer-groovy/src/test/java/org/apache/streampipes/connect/transformer/groovy/GroovyScriptEngineTest.java deleted file mode 100644 index fa321b24c7..0000000000 --- a/streampipes-connect-transformer-groovy/src/test/java/org/apache/streampipes/connect/transformer/groovy/GroovyScriptEngineTest.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * 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.streampipes.connect.transformer.groovy; - -import org.apache.streampipes.client.StreamPipesClient; -import org.apache.streampipes.client.api.IAdapterApi; -import org.apache.streampipes.client.api.IStreamPipesClient; -import org.apache.streampipes.client.api.credentials.CredentialsProvider; -import org.apache.streampipes.connect.transformer.api.Context; -import org.apache.streampipes.connect.transformer.api.exception.ScriptCompilationException; -import org.apache.streampipes.connect.transformer.api.exception.ScriptExecutionException; -import org.apache.streampipes.model.connect.adapter.AdapterDescription; - -import org.junit.jupiter.api.Test; - -import java.lang.reflect.Proxy; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class GroovyScriptEngineTest { - - private final GroovyScriptEngine engine = new GroovyScriptEngine(); - - @Test - void compileAndExecuteSimplePassThroughScript() throws ScriptCompilationException, ScriptExecutionException { - var transformer = engine.compile("out.collect(input)"); - var output = new ArrayList<Map<String, Object>>(); - - transformer.transform( - new LinkedHashMap<>(Map.of("sensor", "machine-1", "value", 42)), - output::add, - null - ); - - assertEquals(1, output.size()); - assertEquals("machine-1", output.get(0).get("sensor")); - assertEquals(42, output.get(0).get("value")); - } - - @Test - void compileAndExecuteFieldReshapingScript() throws ScriptCompilationException, ScriptExecutionException { - var transformer = engine.compile(""" - out.collect([ - reading: [ - sensor: input.sensor, - value: input.value + 1 - ], - tags: [input.tag, "processed"] - ]) - """); - - var output = new ArrayList<Map<String, Object>>(); - transformer.transform( - new LinkedHashMap<>(Map.of("sensor", "machine-2", "value", 9, "tag", "lab")), - output::add, - null - ); - - assertEquals(1, output.size()); - assertEquals(Map.of("sensor", "machine-2", "value", 10), output.get(0).get("reading")); - assertEquals(java.util.List.of("lab", "processed"), output.get(0).get("tags")); - } - - @Test - void executeProvidesOfflineClientThroughContext() throws ScriptCompilationException, ScriptExecutionException { - var transformer = engine.compile(""" - out.collect([ - hasClient: ctx.client() != null - ]) - """); - - var output = new ArrayList<Map<String, Object>>(); - transformer.transform( - Map.of("value", 1), - output::add, - new GroovyScriptContext(offlineClient()) - ); - - assertEquals(1, output.size()); - assertEquals(true, output.get(0).get("hasClient")); - } - - @Test - void executeProvidesContextClientCompatibility() throws ScriptCompilationException, ScriptExecutionException { - var transformer = engine.compile(""" - out.collect([ - ping: ctx.client().ping() - ]) - """); - - var output = new ArrayList<Map<String, Object>>(); - transformer.transform( - Map.of("value", 1), - output::add, - new ScriptContext(new ScriptClient()) - ); - - assertEquals(1, output.size()); - assertEquals("pong", output.get(0).get("ping")); - } - - @Test - void executeCanReadClientResultsInsideVm() throws ScriptCompilationException, ScriptExecutionException { - var transformer = engine.compile(""" - def adapters = ctx.client().adapters().all() - out.collect([ - adapterId: adapters[0].elementId, - adapterName: adapters[0].name - ]) - """); - - var output = executeSingleEvent( - transformer, - Map.of("input", "value"), - new GroovyScriptContext(scriptClientWithAdapters("adapter-groovy", "Groovy Adapter")) - ); - - assertEquals("adapter-groovy", output.get("adapterId")); - assertEquals("Groovy Adapter", output.get("adapterName")); - } - - @Test - void executeCanSendGuestObjectToClient() throws ScriptCompilationException, ScriptExecutionException { - var createdAdapters = new ArrayList<AdapterDescription>(); - var transformer = engine.compile(""" - def adapter = new org.apache.streampipes.model.connect.adapter.AdapterDescription() - adapter.elementId = "created-groovy" - adapter.name = "Created From Groovy" - adapter.running = false - ctx.client().adapters().create(adapter) - out.collect([created: adapter.elementId]) - """); - - var output = executeSingleEvent( - transformer, - Map.of("input", "value"), - new GroovyScriptContext(scriptClientWithCreateRecorder(createdAdapters)) - ); - - assertEquals("created-groovy", output.get("created")); - assertEquals(1, createdAdapters.size()); - assertEquals("created-groovy", createdAdapters.get(0).getElementId()); - assertEquals("Created From Groovy", createdAdapters.get(0).getName()); - assertFalse(createdAdapters.get(0).isRunning()); - } - - @Test - void compileRejectsFileAccess() { - assertSandboxViolation("new File('/tmp/blocked').text"); - } - - @Test - void compileRejectsNioFileAccess() { - assertSandboxViolation("Files.readString(Paths.get('/tmp/blocked'))"); - } - - @Test - void compileRejectsEnvironmentAccess() { - assertSandboxViolation("System.getenv('HOME')"); - } - - @Test - void compileRejectsSystemExit() { - assertSandboxViolation("System.exit(0)"); - } - - @Test - void compileRejectsProcessExecution() { - assertSandboxViolation("Runtime.getRuntime().exec('id')"); - } - - @Test - void compileRejectsNetworkAccess() { - assertSandboxViolation("new Socket('localhost', 80)"); - } - - @Test - void compileRejectsExplicitDangerousImports() { - assertSandboxViolation(""" - import java.lang.reflect.Method - out.collect(input) - """); - } - - @Test - void executeWrapsGroovyRuntimeErrors() throws ScriptCompilationException { - var transformer = engine.compile("throw new IllegalStateException('boom')"); - - var exception = assertThrows( - ScriptExecutionException.class, - () -> transformer.transform(Map.of("value", 1), event -> { - }, null) - ); - - assertEquals("Groovy template execution failed", exception.getMessage()); - assertInstanceOf(Exception.class, exception.getCause()); - } - - private void assertSandboxViolation(String script) { - var exception = assertThrows( - ScriptCompilationException.class, - () -> engine.compile(script) - ); - - assertTrue(exception.getMessage().contains("sandbox restrictions")); - } - - private static Map<String, Object> executeSingleEvent( - org.apache.streampipes.connect.transformer.api.ScriptTransformer transformer, - Map<String, Object> input, - Context context - ) throws ScriptExecutionException { - var output = new ArrayList<Map<String, Object>>(); - transformer.transform(new LinkedHashMap<>(input), output::add, context); - return output.get(0); - } - - private static CredentialsProvider emptyCredentials() { - return List::of; - } - - private static StreamPipesClient offlineClient() { - return StreamPipesClient.create("localhost", 1, emptyCredentials(), true); - } - - private static IStreamPipesClient scriptClientWithAdapters(String elementId, String name) { - var adapter = new AdapterDescription(); - adapter.setElementId(elementId); - adapter.setName(name); - return scriptClient(adapterApi(List.of(adapter), null)); - } - - private static IStreamPipesClient scriptClientWithCreateRecorder(List<AdapterDescription> createdAdapters) { - return scriptClient(adapterApi(List.of(), createdAdapters)); - } - - private static IStreamPipesClient scriptClient(IAdapterApi adapterApi) { - return (IStreamPipesClient) Proxy.newProxyInstance( - GroovyScriptEngineTest.class.getClassLoader(), - new Class<?>[]{IStreamPipesClient.class}, - (proxy, method, args) -> switch (method.getName()) { - case "adapters" -> adapterApi; - case "getCredentials" -> emptyCredentials(); - case "getConfig", "getConnectionConfig" -> null; - case "toString" -> "ScriptClientProxy"; - case "hashCode" -> System.identityHashCode(proxy); - case "equals" -> proxy == args[0]; - default -> throw new UnsupportedOperationException("Unsupported client method: " + method.getName()); - } - ); - } - - private static IAdapterApi adapterApi(List<AdapterDescription> adapters, List<AdapterDescription> createdAdapters) { - return (IAdapterApi) Proxy.newProxyInstance( - GroovyScriptEngineTest.class.getClassLoader(), - new Class<?>[]{IAdapterApi.class}, - (proxy, method, args) -> switch (method.getName()) { - case "all" -> adapters; - case "create" -> { - if (createdAdapters != null) { - createdAdapters.add(new AdapterDescription((AdapterDescription) args[0])); - } - yield null; - } - case "get" -> java.util.Optional.empty(); - case "delete", "update" -> null; - case "toString" -> "AdapterApiProxy"; - case "hashCode" -> System.identityHashCode(proxy); - case "equals" -> proxy == args[0]; - default -> throw new UnsupportedOperationException("Unsupported adapter method: " + method.getName()); - } - ); - } - - private static final class ScriptContext implements Context { - - private final ScriptClient client; - - private ScriptContext(ScriptClient client) { - this.client = client; - } - - public ScriptClient client() { - return client; - } - } - - private static final class ScriptClient { - - public String ping() { - return "pong"; - } - } -}
