This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/master by this push:
new f77438f23a Hardening on Groovy sandbox against filesystem and
execution bypasses (#1399)
f77438f23a is described below
commit f77438f23a90f646e771580b78e7da5512c5c539
Author: Massimiliano Perrone <[email protected]>
AuthorDate: Fri May 29 09:49:36 2026 +0200
Hardening on Groovy sandbox against filesystem and execution bypasses
(#1399)
---
.../src/main/resources/META-INF/groovy.blacklist | 140 ++++++++
.../spring/implementation/GroovySandboxTest.java | 396 +++++++++++++++++++--
.../BeansExpressionRuntimeExecMacroActions.groovy | 35 ++
.../resources/BeansOverloadsMacroActions.groovy | 43 +++
.../BeansStatementSystemExitMacroActions.groovy | 31 ++
.../resources/EvalOverloadsMacroActions.groovy | 34 ++
.../resources/EvalRuntimeExecMacroActions.groovy | 31 ++
.../FileSystemProviderOverloadsMacroActions.groovy | 47 +++
...FileSystemProviderReadStringMacroActions.groovy | 32 ++
.../FileSystemsOverloadsMacroActions.groovy | 72 ++++
.../FileSystemsReadStringMacroActions.groovy | 31 ++
.../FilesCreateTempDirectoryMacroActions.groovy | 31 ++
.../FilesCreateTempFileMacroActions.groovy | 32 ++
.../GroovyClassLoaderOverloadsMacroActions.groovy | 40 +++
.../GroovyShellOverloadsMacroActions.groovy | 43 +++
.../GroovyShellRuntimeExecMacroActions.groovy | 31 ++
.../MethodHandlesOverloadsMacroActions.groovy | 59 +++
.../MethodHandlesRuntimeExecMacroActions.groovy | 43 +++
.../PathOfFilesReadStringMacroActions.groovy | 31 ++
.../PathOfFilesWriteStringMacroActions.groovy | 30 ++
.../PathOfUriFilesReadStringMacroActions.groovy | 31 ++
.../PathsGetFilesReadStringMacroActions.groovy | 31 ++
.../PathsGetUriFilesReadStringMacroActions.groovy | 31 ++
.../ProcessBuilderStartPipelineMacroActions.groovy | 31 ++
.../test/resources/RuntimeExecMacroActions.groovy | 31 ++
.../RuntimeExecOverloadsMacroActions.groovy | 42 +++
.../ScriptEngineOverloadsMacroActions.groovy | 36 ++
27 files changed, 1441 insertions(+), 24 deletions(-)
diff --git a/core/spring/src/main/resources/META-INF/groovy.blacklist
b/core/spring/src/main/resources/META-INF/groovy.blacklist
index 85292bc608..cfe5dfeccd 100644
--- a/core/spring/src/main/resources/META-INF/groovy.blacklist
+++ b/core/spring/src/main/resources/META-INF/groovy.blacklist
@@ -16,17 +16,90 @@
# under the License.
# Reflective access to Groovy
+new groovy.lang.GroovyClassLoader
+new groovy.lang.GroovyClassLoader java.lang.ClassLoader
+new groovy.lang.GroovyClassLoader groovy.lang.GroovyClassLoader
+new groovy.lang.GroovyClassLoader java.lang.ClassLoader
org.codehaus.groovy.control.CompilerConfiguration
+new groovy.lang.GroovyClassLoader java.lang.ClassLoader
org.codehaus.groovy.control.CompilerConfiguration boolean
+new groovy.lang.GroovyShell
+new groovy.lang.GroovyShell groovy.lang.Binding
+new groovy.lang.GroovyShell groovy.lang.Binding
org.codehaus.groovy.control.CompilerConfiguration
+new groovy.lang.GroovyShell groovy.lang.GroovyShell
+new groovy.lang.GroovyShell java.lang.ClassLoader
+new groovy.lang.GroovyShell java.lang.ClassLoader groovy.lang.Binding
+new groovy.lang.GroovyShell java.lang.ClassLoader groovy.lang.Binding
org.codehaus.groovy.control.CompilerConfiguration
+new groovy.lang.GroovyShell java.lang.ClassLoader
org.codehaus.groovy.control.CompilerConfiguration
+new groovy.lang.GroovyShell org.codehaus.groovy.control.CompilerConfiguration
+method groovy.lang.GroovyClassLoader addClasspath java.lang.String
+method groovy.lang.GroovyClassLoader addURL java.net.URL
+method groovy.lang.GroovyClassLoader defineClass java.lang.String byte[]
+method groovy.lang.GroovyClassLoader parseClass groovy.lang.GroovyCodeSource
+method groovy.lang.GroovyClassLoader parseClass groovy.lang.GroovyCodeSource
boolean
+method groovy.lang.GroovyClassLoader parseClass java.io.File
+method groovy.lang.GroovyClassLoader parseClass java.io.Reader java.lang.String
+method groovy.lang.GroovyClassLoader parseClass java.lang.String
+method groovy.lang.GroovyClassLoader parseClass java.lang.String
java.lang.String
+method groovy.lang.GroovyShell evaluate groovy.lang.GroovyCodeSource
+method groovy.lang.GroovyShell evaluate java.io.File
+method groovy.lang.GroovyShell evaluate java.io.Reader
+method groovy.lang.GroovyShell evaluate java.io.Reader java.lang.String
+method groovy.lang.GroovyShell evaluate java.lang.String
+method groovy.lang.GroovyShell evaluate java.lang.String java.lang.String
+method groovy.lang.GroovyShell evaluate java.lang.String java.lang.String
java.lang.String
+method groovy.lang.GroovyShell evaluate java.net.URI
+method groovy.lang.GroovyShell parse groovy.lang.GroovyCodeSource
+method groovy.lang.GroovyShell parse groovy.lang.GroovyCodeSource
groovy.lang.Binding
+method groovy.lang.GroovyShell parse java.io.File
+method groovy.lang.GroovyShell parse java.io.Reader
+method groovy.lang.GroovyShell parse java.io.Reader groovy.lang.Binding
+method groovy.lang.GroovyShell parse java.io.Reader java.lang.String
+method groovy.lang.GroovyShell parse java.io.Reader java.lang.String
groovy.lang.Binding
+method groovy.lang.GroovyShell parse java.lang.String
+method groovy.lang.GroovyShell parse java.lang.String groovy.lang.Binding
+method groovy.lang.GroovyShell parse java.lang.String java.lang.String
+method groovy.lang.GroovyShell parse java.lang.String java.lang.String
groovy.lang.Binding
+method groovy.lang.GroovyShell parse java.net.URI
+method groovy.lang.GroovyShell run groovy.lang.GroovyCodeSource
java.lang.String[]
+method groovy.lang.GroovyShell run groovy.lang.GroovyCodeSource java.util.List
+method groovy.lang.GroovyShell run java.io.File java.lang.String[]
+method groovy.lang.GroovyShell run java.io.File java.util.List
+method groovy.lang.GroovyShell run java.io.Reader java.lang.String
java.lang.String[]
+method groovy.lang.GroovyShell run java.io.Reader java.lang.String
java.util.List
+method groovy.lang.GroovyShell run java.lang.String java.lang.String
java.lang.String[]
+method groovy.lang.GroovyShell run java.lang.String java.lang.String
java.util.List
+method groovy.lang.GroovyShell run java.net.URI java.lang.String[]
+method groovy.lang.GroovyShell run java.net.URI java.util.List
+staticMethod groovy.lang.GroovyShell withConfig groovy.lang.Closure
+staticMethod groovy.util.Eval me java.lang.String
+staticMethod groovy.util.Eval me java.lang.String java.lang.Object
java.lang.String
+staticMethod groovy.util.Eval x java.lang.Object java.lang.String
+staticMethod groovy.util.Eval xy java.lang.Object java.lang.Object
java.lang.String
+staticMethod groovy.util.Eval xyz java.lang.Object java.lang.Object
java.lang.Object java.lang.String
method groovy.lang.GroovyObject getMetaClass
method groovy.lang.GroovyObject getProperty java.lang.String
method groovy.lang.GroovyObject invokeMethod java.lang.String java.lang.Object
method groovy.lang.GroovyObject setMetaClass groovy.lang.MetaClass
method groovy.lang.GroovyObject setProperty java.lang.String java.lang.Object
+# Runtime script engines could evaluate code outside the Groovy sandbox
transformer.
+new javax.script.ScriptEngineManager
+new javax.script.ScriptEngineManager java.lang.ClassLoader
+method javax.script.ScriptEngine eval java.io.Reader
+method javax.script.ScriptEngine eval java.io.Reader javax.script.Bindings
+method javax.script.ScriptEngine eval java.io.Reader javax.script.ScriptContext
+method javax.script.ScriptEngine eval java.lang.String
+method javax.script.ScriptEngine eval java.lang.String javax.script.Bindings
+method javax.script.ScriptEngine eval java.lang.String
javax.script.ScriptContext
+method javax.script.ScriptEngineManager getEngineByExtension java.lang.String
+method javax.script.ScriptEngineManager getEngineByMimeType java.lang.String
+method javax.script.ScriptEngineManager getEngineByName java.lang.String
+
# Raw file operations
staticMethod java.io.File createTempFile java.lang.String java.lang.String
staticMethod java.io.File createTempFile java.lang.String java.lang.String
java.io.File
new java.io.File java.lang.String
new java.io.File java.lang.String java.lang.String
+new java.io.File java.io.File java.lang.String
new java.io.File java.net.URI
staticMethod java.io.File listRoots
new java.io.FileInputStream java.lang.String
@@ -61,8 +134,19 @@ method java.lang.Class newInstance
# Same for local process execution.
new java.lang.ProcessBuilder java.lang.String[]
new java.lang.ProcessBuilder java.util.List
+staticMethod java.lang.ProcessBuilder startPipeline java.util.List
method java.lang.Process start
staticMethod java.lang.Runtime getRuntime
+method java.lang.Runtime exec java.lang.String
+method java.lang.Runtime exec java.lang.String java.lang.String[]
+method java.lang.Runtime exec java.lang.String java.lang.String[] java.io.File
+method java.lang.Runtime exec java.lang.String[]
+method java.lang.Runtime exec java.lang.String[] java.lang.String[]
+method java.lang.Runtime exec java.lang.String[] java.lang.String[]
java.io.File
+method java.lang.Runtime exit int
+method java.lang.Runtime halt int
+method java.lang.Runtime load java.lang.String
+method java.lang.Runtime loadLibrary java.lang.String
staticMethod java.lang.System exit int
# Leak information.
@@ -83,6 +167,62 @@ method java.net.URL openStream
# NIO file operations must start with a Path:
staticMethod java.nio.file.Paths get java.lang.String java.lang.String[]
staticMethod java.nio.file.Paths get java.net.URI
+staticMethod java.nio.file.Path of java.lang.String java.lang.String[]
+staticMethod java.nio.file.Path of java.net.URI
+staticMethod java.nio.file.FileSystems getDefault
+staticMethod java.nio.file.FileSystems getFileSystem java.net.URI
+staticMethod java.nio.file.FileSystems newFileSystem java.net.URI java.util.Map
+staticMethod java.nio.file.FileSystems newFileSystem java.net.URI
java.util.Map java.lang.ClassLoader
+staticMethod java.nio.file.FileSystems newFileSystem java.nio.file.Path
+staticMethod java.nio.file.FileSystems newFileSystem java.nio.file.Path
java.lang.ClassLoader
+staticMethod java.nio.file.FileSystems newFileSystem java.nio.file.Path
java.util.Map
+staticMethod java.nio.file.FileSystems newFileSystem java.nio.file.Path
java.util.Map java.lang.ClassLoader
+method java.nio.file.FileSystem getPath java.lang.String java.lang.String[]
+staticMethod java.nio.file.Files createTempDirectory java.lang.String
java.nio.file.attribute.FileAttribute[]
+staticMethod java.nio.file.Files createTempFile java.lang.String
java.lang.String java.nio.file.attribute.FileAttribute[]
+staticMethod java.nio.file.spi.FileSystemProvider installedProviders
+method java.nio.file.spi.FileSystemProvider getFileSystem java.net.URI
+method java.nio.file.spi.FileSystemProvider getPath java.net.URI
+method java.nio.file.spi.FileSystemProvider newFileSystem java.net.URI
java.util.Map
+method java.nio.file.spi.FileSystemProvider newFileSystem java.nio.file.Path
java.util.Map
+
+# Indirect method invocation
+staticMethod java.lang.invoke.MethodHandles lookup
+staticMethod java.lang.invoke.MethodHandles privateLookupIn java.lang.Class
java.lang.invoke.MethodHandles$Lookup
+staticMethod java.lang.invoke.MethodHandles publicLookup
+staticMethod java.lang.invoke.MethodHandles reflectAs java.lang.Class
java.lang.invoke.MethodHandle
+method java.lang.invoke.MethodHandle invoke java.lang.Object[]
+method java.lang.invoke.MethodHandle invokeExact java.lang.Object[]
+method java.lang.invoke.MethodHandle invokeWithArguments java.lang.Object[]
+method java.lang.invoke.MethodHandle invokeWithArguments java.util.List
+method java.lang.invoke.MethodHandles$Lookup bind java.lang.Object
java.lang.String java.lang.invoke.MethodType
+method java.lang.invoke.MethodHandles$Lookup defineClass byte[]
+method java.lang.invoke.MethodHandles$Lookup defineHiddenClass byte[] boolean
java.lang.invoke.MethodHandles$Lookup$ClassOption[]
+method java.lang.invoke.MethodHandles$Lookup defineHiddenClassWithClassData
byte[] java.lang.Object boolean
java.lang.invoke.MethodHandles$Lookup$ClassOption[]
+method java.lang.invoke.MethodHandles$Lookup findClass java.lang.String
+method java.lang.invoke.MethodHandles$Lookup findConstructor java.lang.Class
java.lang.invoke.MethodType
+method java.lang.invoke.MethodHandles$Lookup findGetter java.lang.Class
java.lang.String java.lang.Class
+method java.lang.invoke.MethodHandles$Lookup findSetter java.lang.Class
java.lang.String java.lang.Class
+method java.lang.invoke.MethodHandles$Lookup findSpecial java.lang.Class
java.lang.String java.lang.invoke.MethodType java.lang.Class
+method java.lang.invoke.MethodHandles$Lookup findStatic java.lang.Class
java.lang.String java.lang.invoke.MethodType
+method java.lang.invoke.MethodHandles$Lookup findStaticGetter java.lang.Class
java.lang.String java.lang.Class
+method java.lang.invoke.MethodHandles$Lookup findStaticSetter java.lang.Class
java.lang.String java.lang.Class
+method java.lang.invoke.MethodHandles$Lookup findStaticVarHandle
java.lang.Class java.lang.String java.lang.Class
+method java.lang.invoke.MethodHandles$Lookup findVarHandle java.lang.Class
java.lang.String java.lang.Class
+method java.lang.invoke.MethodHandles$Lookup findVirtual java.lang.Class
java.lang.String java.lang.invoke.MethodType
+method java.lang.invoke.MethodHandles$Lookup unreflect java.lang.reflect.Method
+method java.lang.invoke.MethodHandles$Lookup unreflectConstructor
java.lang.reflect.Constructor
+method java.lang.invoke.MethodHandles$Lookup unreflectGetter
java.lang.reflect.Field
+method java.lang.invoke.MethodHandles$Lookup unreflectSetter
java.lang.reflect.Field
+method java.lang.invoke.MethodHandles$Lookup unreflectSpecial
java.lang.reflect.Method java.lang.Class
+method java.lang.invoke.MethodHandles$Lookup unreflectVarHandle
java.lang.reflect.Field
+new java.beans.Expression java.lang.Object java.lang.String java.lang.Object[]
+new java.beans.Expression java.lang.Object java.lang.Object java.lang.String
java.lang.Object[]
+new java.beans.Statement java.lang.Object java.lang.String java.lang.Object[]
+method java.beans.Expression execute
+method java.beans.Expression getValue
+method java.beans.Expression setValue java.lang.Object
+method java.beans.Statement execute
# More process execution, Groovy-style:
staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods execute
java.lang.String
diff --git
a/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/GroovySandboxTest.java
b/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/GroovySandboxTest.java
index 8293b3c696..fe2e0641bf 100644
---
a/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/GroovySandboxTest.java
+++
b/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/GroovySandboxTest.java
@@ -23,6 +23,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.cxf.helpers.IOUtils;
import org.apache.syncope.common.lib.types.ImplementationEngine;
@@ -32,53 +37,396 @@ import
org.apache.syncope.core.spring.SpringTestConfiguration;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;
+import org.junit.jupiter.api.io.TempDir;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@SpringJUnitConfig(classes = { SpringTestConfiguration.class })
class GroovySandboxTest {
+ @TempDir
+ private Path tempDir;
+
@Test
void processBuilder() throws Exception {
- Implementation impl = mock(Implementation.class);
- when(impl.getKey()).thenReturn("processBuilder");
- when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY);
- when(impl.getBody()).thenReturn(IOUtils.toString(
-
getClass().getResourceAsStream("/ProcessBuilderMacroActions.groovy")));
+ final MacroActions actions = actions("processBuilder",
"/ProcessBuilderMacroActions.groovy");
- MacroActions actions = ImplementationManager.build(impl);
-
- SecurityException e = assertThrows(
+ final SecurityException e = assertThrows(
SecurityException.class, () -> actions.afterAll(null, new
StringBuilder()));
assertTrue(e.getMessage().contains("Insecure call to 'new
java.lang.ProcessBuilder java.lang.String[]'"));
}
- @EnabledOnOs(OS.LINUX)
- @Test
- void bash() throws Exception {
- Implementation impl = mock(Implementation.class);
- when(impl.getKey()).thenReturn("bash");
+ private MacroActions actions(final String key, final String resource)
throws Exception {
+ final Implementation impl = mock(Implementation.class);
+ when(impl.getKey()).thenReturn(key);
when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY);
when(impl.getBody()).thenReturn(IOUtils.toString(
- getClass().getResourceAsStream("/BashMacroActions.groovy")));
+
Objects.requireNonNull(getClass().getResourceAsStream(resource))));
- MacroActions actions = ImplementationManager.build(impl);
+ return ImplementationManager.build(impl);
+ }
- SecurityException e = assertThrows(
+ @EnabledOnOs(OS.LINUX)
+ @Test
+ void bash() throws Exception {
+ final MacroActions actions = actions("bash",
"/BashMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
SecurityException.class, () -> actions.afterAll(null, new
StringBuilder()));
assertTrue(e.getMessage().contains("Insecure call to 'new java.io.File
java.lang.String'"));
}
@Test
void staticMacroActions() throws Exception {
- Implementation impl = mock(Implementation.class);
- when(impl.getKey()).thenReturn("staticMacroActions");
- when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY);
- when(impl.getBody()).thenReturn(IOUtils.toString(
- getClass().getResourceAsStream("/StaticMacroActions.groovy")));
-
- BeanCreationException e = assertThrows(BeanCreationException.class, ()
-> ImplementationManager.build(impl));
- SecurityException sec = (SecurityException)
ExceptionUtils.getRootCause(e);
+ final BeanCreationException e =
+ assertThrows(
+ BeanCreationException.class,
+ () -> actions("staticMacroActions",
"/StaticMacroActions.groovy"));
+ final SecurityException sec = (SecurityException)
ExceptionUtils.getRootCause(e);
assertTrue(sec.getMessage().startsWith("Insecure call to 'new
java.lang.ProcessBuilder java.util.List'"));
}
+
+ @Test
+ void pathOfFilesReadString() throws Exception {
+ final Path testFile = tempDir.resolve("sandbox-read.txt");
+ Files.writeString(testFile, "sandbox-read-ok");
+
+ final MacroActions actions = actions("pathOfFilesReadString",
"/PathOfFilesReadStringMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new
StringBuilder(testFile.toAbsolutePath().toString())));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'staticMethod java.nio.file.Path of
java.lang.String java.lang.String[]'"));
+ }
+
+ @Test
+ void pathOfFilesWriteString() throws Exception {
+ final Path testFile = tempDir.resolve("sandbox-write.txt");
+
+ final MacroActions actions = actions("pathOfFilesWriteString",
"/PathOfFilesWriteStringMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new
StringBuilder(testFile.toAbsolutePath().toString())));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'staticMethod java.nio.file.Path of
java.lang.String java.lang.String[]'"));
+ }
+
+ @Test
+ void pathOfUriFilesReadString() throws Exception {
+ final Path testFile = tempDir.resolve("sandbox-read-uri.txt");
+ Files.writeString(testFile, "sandbox-read-uri-ok");
+
+ final MacroActions actions =
+ actions("pathOfUriFilesReadString",
"/PathOfUriFilesReadStringMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new
StringBuilder(testFile.toUri().toString())));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'staticMethod java.nio.file.Path of
java.net.URI'"));
+ }
+
+ @Test
+ void pathsGetFilesReadString() throws Exception {
+ final Path testFile = tempDir.resolve("sandbox-paths-get-read.txt");
+ Files.writeString(testFile, "sandbox-paths-get-read-ok");
+
+ final MacroActions actions = actions("pathsGetFilesReadString",
"/PathsGetFilesReadStringMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new
StringBuilder(testFile.toAbsolutePath().toString())));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'staticMethod java.nio.file.Paths get
java.lang.String java.lang.String[]'"));
+ }
+
+ @Test
+ void pathsGetUriFilesReadString() throws Exception {
+ final Path testFile =
tempDir.resolve("sandbox-paths-get-uri-read.txt");
+ Files.writeString(testFile, "sandbox-paths-get-uri-read-ok");
+
+ final MacroActions actions =
+ actions("pathsGetUriFilesReadString",
"/PathsGetUriFilesReadStringMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new
StringBuilder(testFile.toUri().toString())));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'staticMethod java.nio.file.Paths get
java.net.URI'"));
+ }
+
+ @Test
+ void fileSystemsReadString() throws Exception {
+ final Path testFile = tempDir.resolve("sandbox-filesystems-read.txt");
+ Files.writeString(testFile, "sandbox-filesystems-read-ok");
+
+ final MacroActions actions = actions("fileSystemsReadString",
"/FileSystemsReadStringMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new
StringBuilder(testFile.toAbsolutePath().toString())));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'staticMethod java.nio.file.FileSystems
getDefault'"));
+ }
+
+ @Test
+ void fileSystemProviderReadString() throws Exception {
+ final Path testFile =
tempDir.resolve("sandbox-filesystem-provider-read.txt");
+ Files.writeString(testFile, "sandbox-filesystem-provider-read-ok");
+
+ final MacroActions actions =
+ actions("fileSystemProviderReadString",
"/FileSystemProviderReadStringMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new
StringBuilder(testFile.toUri().toString())));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'staticMethod
java.nio.file.spi.FileSystemProvider installedProviders'"));
+ }
+
+ @Test
+ void filesCreateTempFile() throws Exception {
+ final MacroActions actions = actions("filesCreateTempFile",
"/FilesCreateTempFileMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new StringBuilder()));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'staticMethod java.nio.file.Files
createTempFile "
+ + "java.lang.String java.lang.String
java.nio.file.attribute.FileAttribute[]'"));
+ }
+
+ @Test
+ void filesCreateTempDirectory() throws Exception {
+ final MacroActions actions =
+ actions("filesCreateTempDirectory",
"/FilesCreateTempDirectoryMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new StringBuilder()));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'staticMethod java.nio.file.Files
createTempDirectory "
+ + "java.lang.String
java.nio.file.attribute.FileAttribute[]'"));
+ }
+
+ @EnabledOnOs(OS.LINUX)
+ @Test
+ void processBuilderStartPipeline() throws Exception {
+ final MacroActions actions =
+ actions("processBuilderStartPipeline",
"/ProcessBuilderStartPipelineMacroActions.groovy");
+
+ @SuppressWarnings("unchecked")
+ final Map<String, java.io.Serializable> ctx =
+ (Map<String, java.io.Serializable>) (Map<?, ?>) Map.of(
+ "builders",
+ List.of(new ProcessBuilder("/bin/sh", "-c", "printf
sandbox-start-pipeline-ok")));
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(ctx, new StringBuilder()));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'staticMethod java.lang.ProcessBuilder
startPipeline java.util.List'"));
+ }
+
+ @EnabledOnOs(OS.LINUX)
+ @Test
+ void runtimeExec() throws Exception {
+ final MacroActions actions = actions("runtimeExec",
"/RuntimeExecMacroActions.groovy");
+
+ @SuppressWarnings("unchecked")
+ final Map<String, java.io.Serializable> ctx =
+ (Map<String, java.io.Serializable>) (Map<?, ?>)
Map.of("runtime", Runtime.getRuntime());
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(ctx, new StringBuilder()));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'method java.lang.Runtime exec
java.lang.String[]'"));
+ }
+
+ @EnabledOnOs(OS.LINUX)
+ @Test
+ void beansExpressionRuntimeExec() throws Exception {
+ final MacroActions actions =
+ actions("beansExpressionRuntimeExec",
"/BeansExpressionRuntimeExecMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new StringBuilder()));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'new java.beans.Expression java.lang.Object
java.lang.String java.lang.Object[]'"));
+ }
+
+ @Test
+ void beansStatementSystemExit() throws Exception {
+ final MacroActions actions =
+ actions("beansStatementSystemExit",
"/BeansStatementSystemExitMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new StringBuilder()));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'new java.beans.Statement java.lang.Object
java.lang.String java.lang.Object[]'"));
+ }
+
+ @EnabledOnOs(OS.LINUX)
+ @Test
+ void groovyShellRuntimeExec() throws Exception {
+ final MacroActions actions = actions("groovyShellRuntimeExec",
"/GroovyShellRuntimeExecMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new StringBuilder()));
+ assertTrue(e.getMessage().contains("Insecure call to 'new
groovy.lang.GroovyShell'"));
+ }
+
+ @EnabledOnOs(OS.LINUX)
+ @Test
+ void evalRuntimeExec() throws Exception {
+ final MacroActions actions = actions("evalRuntimeExec",
"/EvalRuntimeExecMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new StringBuilder()));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'staticMethod groovy.util.Eval me
java.lang.String'"));
+ }
+
+ @EnabledOnOs(OS.LINUX)
+ @Test
+ void methodHandlesRuntimeExec() throws Exception {
+ final MacroActions actions =
+ actions("methodHandlesRuntimeExec",
"/MethodHandlesRuntimeExecMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new StringBuilder()));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'staticMethod java.lang.invoke.MethodHandles
publicLookup'"));
+ }
+
+ @EnabledOnOs(OS.LINUX)
+ @Test
+ void runtimeExecOverloads() throws Exception {
+ final MacroActions actions = actions("runtimeExecOverloads",
"/RuntimeExecOverloadsMacroActions.groovy");
+
+ @SuppressWarnings("unchecked")
+ final Map<String, java.io.Serializable> ctx =
+ (Map<String, java.io.Serializable>) (Map<?, ?>)
Map.of("runtime", Runtime.getRuntime());
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(ctx, new StringBuilder()));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'method java.lang.Runtime exec
java.lang.String'"));
+ }
+
+ @Test
+ void evalOverloads() throws Exception {
+ final MacroActions actions = actions("evalOverloads",
"/EvalOverloadsMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new StringBuilder()));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'staticMethod groovy.util.Eval me
java.lang.String'"));
+ }
+
+ @Test
+ void groovyShellOverloads() throws Exception {
+ final Path script =
tempDir.resolve("sandbox-groovy-shell-overload.groovy");
+ Files.writeString(script, "return 'shell-evaluate-file|'");
+ @SuppressWarnings("unchecked")
+ final Map<String, java.io.Serializable> ctx =
+ (Map<String, java.io.Serializable>) (Map<?, ?>)
Map.of("scriptFile", script.toFile());
+
+ final MacroActions actions = actions("groovyShellOverloads",
"/GroovyShellOverloadsMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(ctx, new StringBuilder()));
+ assertTrue(e.getMessage().contains("Insecure call to 'new
groovy.lang.GroovyShell'"));
+ }
+
+ @Test
+ void groovyClassLoaderOverloads() throws Exception {
+ final MacroActions actions =
+ actions("groovyClassLoaderOverloads",
"/GroovyClassLoaderOverloadsMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new StringBuilder()));
+ assertTrue(e.getMessage().contains("Insecure call to 'new
groovy.lang.GroovyClassLoader'"));
+ }
+
+ @Test
+ void scriptEngineOverloads() throws Exception {
+ final MacroActions actions = actions("scriptEngineOverloads",
"/ScriptEngineOverloadsMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new StringBuilder()));
+ assertTrue(e.getMessage().contains("Insecure call to 'new
javax.script.ScriptEngineManager'"));
+ }
+
+ @Test
+ void fileSystemsOverloads() throws Exception {
+ final Path file = tempDir.resolve("sandbox-filesystems-overloads.txt");
+ Files.writeString(file, "sandbox-filesystems-overloads-ok");
+ @SuppressWarnings("unchecked")
+ final Map<String, java.io.Serializable> ctx =
+ (Map<String, java.io.Serializable>) (Map<?, ?>) Map.of("path",
file);
+
+ final MacroActions actions = actions("fileSystemsOverloads",
"/FileSystemsOverloadsMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(ctx, new StringBuilder()));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'staticMethod java.nio.file.FileSystems
getFileSystem java.net.URI'"));
+ }
+
+ @Test
+ void fileSystemProviderOverloads() throws Exception {
+ final Path file =
tempDir.resolve("sandbox-filesystem-provider-overloads.txt");
+ Files.writeString(file, "sandbox-filesystem-provider-overloads-ok");
+ @SuppressWarnings("unchecked")
+ final Map<String, java.io.Serializable> ctx =
+ (Map<String, java.io.Serializable>) (Map<?, ?>) Map.of(
+ "path", file,
+ "provider", file.getFileSystem().provider());
+
+ final MacroActions actions =
+ actions("fileSystemProviderOverloads",
"/FileSystemProviderOverloadsMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(ctx, new StringBuilder()));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'method java.nio.file.spi.FileSystemProvider
getFileSystem java.net.URI'"));
+ }
+
+ @Test
+ void methodHandlesOverloads() throws Exception {
+ final MacroActions actions = actions("methodHandlesOverloads",
"/MethodHandlesOverloadsMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new StringBuilder()));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'staticMethod java.lang.invoke.MethodHandles
lookup'"));
+ }
+
+ @Test
+ void beansOverloads() throws Exception {
+ final MacroActions actions = actions("beansOverloads",
"/BeansOverloadsMacroActions.groovy");
+
+ final SecurityException e = assertThrows(
+ SecurityException.class,
+ () -> actions.afterAll(null, new StringBuilder()));
+ assertTrue(e.getMessage().contains(
+ "Insecure call to 'new java.beans.Expression java.lang.Object
java.lang.String java.lang.Object[]'"));
+ }
}
diff --git
a/core/spring/src/test/resources/BeansExpressionRuntimeExecMacroActions.groovy
b/core/spring/src/test/resources/BeansExpressionRuntimeExecMacroActions.groovy
new file mode 100644
index 0000000000..494f0925fc
--- /dev/null
+++
b/core/spring/src/test/resources/BeansExpressionRuntimeExecMacroActions.groovy
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class BeansExpressionRuntimeExecMacroActions extends Script
implements MacroActions {}
+@BaseScript BeansExpressionRuntimeExecMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def runtime = new java.beans.Expression(java.lang.Runtime, 'getRuntime', []
as Object[]).value
+ def process = new java.beans.Expression(
+ runtime,
+ 'exec',
+ [['/bin/sh', '-c', 'printf sandbox-beans-expression-ok'] as String[]] as
Object[]).value
+ output.append(': ').append(process.inputStream.text)
+ return output
+}
diff --git a/core/spring/src/test/resources/BeansOverloadsMacroActions.groovy
b/core/spring/src/test/resources/BeansOverloadsMacroActions.groovy
new file mode 100644
index 0000000000..7c7543a1b8
--- /dev/null
+++ b/core/spring/src/test/resources/BeansOverloadsMacroActions.groovy
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class BeansOverloadsMacroActions extends Script implements
MacroActions {}
+@BaseScript BeansOverloadsMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def expression = new java.beans.Expression('beans-', 'concat',
['expression|'] as Object[])
+ output.append(expression.value)
+
+ def preset = new java.beans.Expression(null, 'beans-', 'concat', ['preset|']
as Object[])
+ preset.execute()
+ output.append(preset.value)
+ preset.setValue('beans-set-value|')
+ output.append(preset.value)
+
+ def buffer = new StringBuffer()
+ def statement = new java.beans.Statement(buffer, 'append',
['beans-statement-execute|'] as Object[])
+ statement.execute()
+ output.append(buffer.toString())
+
+ return output
+}
diff --git
a/core/spring/src/test/resources/BeansStatementSystemExitMacroActions.groovy
b/core/spring/src/test/resources/BeansStatementSystemExitMacroActions.groovy
new file mode 100644
index 0000000000..1d5256d8ec
--- /dev/null
+++ b/core/spring/src/test/resources/BeansStatementSystemExitMacroActions.groovy
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class BeansStatementSystemExitMacroActions extends Script implements
MacroActions {}
+@BaseScript BeansStatementSystemExitMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def statement = new java.beans.Statement(java.lang.System, 'exit', [0] as
Object[])
+ output.append(statement.toString())
+ return output
+}
diff --git a/core/spring/src/test/resources/EvalOverloadsMacroActions.groovy
b/core/spring/src/test/resources/EvalOverloadsMacroActions.groovy
new file mode 100644
index 0000000000..473925e271
--- /dev/null
+++ b/core/spring/src/test/resources/EvalOverloadsMacroActions.groovy
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class EvalOverloadsMacroActions extends Script implements
MacroActions {}
+@BaseScript EvalOverloadsMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ output.append(groovy.util.Eval.me("'eval-me0|'"))
+ output.append(groovy.util.Eval.me('value', 'eval-me1|', 'value'))
+ output.append(groovy.util.Eval.x('eval-x|', 'x'))
+ output.append(groovy.util.Eval.xy('eval-', 'xy|', 'x + y'))
+ output.append(groovy.util.Eval.xyz('eval-', 'x', 'yz|', 'x + y + z'))
+ return output
+}
diff --git a/core/spring/src/test/resources/EvalRuntimeExecMacroActions.groovy
b/core/spring/src/test/resources/EvalRuntimeExecMacroActions.groovy
new file mode 100644
index 0000000000..fd62046bbb
--- /dev/null
+++ b/core/spring/src/test/resources/EvalRuntimeExecMacroActions.groovy
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class EvalRuntimeExecMacroActions extends Script implements
MacroActions {}
+@BaseScript EvalRuntimeExecMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ output.append(groovy.util.Eval.me(
+ "java.lang.Runtime.getRuntime().exec(['/bin/sh','-c','printf
sandbox-eval-ok'] as String[]).inputStream.text"))
+ return output
+}
diff --git
a/core/spring/src/test/resources/FileSystemProviderOverloadsMacroActions.groovy
b/core/spring/src/test/resources/FileSystemProviderOverloadsMacroActions.groovy
new file mode 100644
index 0000000000..d224b95508
--- /dev/null
+++
b/core/spring/src/test/resources/FileSystemProviderOverloadsMacroActions.groovy
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class FileSystemProviderOverloadsMacroActions extends Script
implements MacroActions {}
+@BaseScript FileSystemProviderOverloadsMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def provider = ctx.provider as java.nio.file.spi.FileSystemProvider
+ def path = ctx.path as java.nio.file.Path
+
+ provider.getFileSystem(java.net.URI.create('file:///'))
+ output.append('fsp-get-filesystem|')
+
+ try {
+ provider.newFileSystem(java.net.URI.create('file:///'), [:])
+ } catch (java.lang.IllegalArgumentException |
java.nio.file.FileSystemAlreadyExistsException e) {
+ output.append('fsp-new-uri-map|')
+ }
+
+ try {
+ provider.newFileSystem(path, [:])
+ } catch (java.io.IOException | java.lang.UnsupportedOperationException e) {
+ output.append('fsp-new-path-map|')
+ }
+
+ return output
+}
diff --git
a/core/spring/src/test/resources/FileSystemProviderReadStringMacroActions.groovy
b/core/spring/src/test/resources/FileSystemProviderReadStringMacroActions.groovy
new file mode 100644
index 0000000000..0e9cb09af4
--- /dev/null
+++
b/core/spring/src/test/resources/FileSystemProviderReadStringMacroActions.groovy
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class FileSystemProviderReadStringMacroActions extends Script
implements MacroActions {}
+@BaseScript FileSystemProviderReadStringMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def provider =
java.nio.file.spi.FileSystemProvider.installedProviders().find { it.scheme ==
'file' }
+ def path = provider.getPath(java.net.URI.create(output.toString()))
+ output.append(': ').append(java.nio.file.Files.readString(path))
+ return output
+}
diff --git
a/core/spring/src/test/resources/FileSystemsOverloadsMacroActions.groovy
b/core/spring/src/test/resources/FileSystemsOverloadsMacroActions.groovy
new file mode 100644
index 0000000000..788f4f45d0
--- /dev/null
+++ b/core/spring/src/test/resources/FileSystemsOverloadsMacroActions.groovy
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class FileSystemsOverloadsMacroActions extends Script implements
MacroActions {}
+@BaseScript FileSystemsOverloadsMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def fileUri = java.net.URI.create('file:///')
+ def unsupportedUri = java.net.URI.create('tmpfs://sandbox')
+ def path = ctx.path as java.nio.file.Path
+
+ java.nio.file.FileSystems.getFileSystem(fileUri)
+ output.append('fs-get-filesystem|')
+
+ try {
+ java.nio.file.FileSystems.newFileSystem(unsupportedUri, [:])
+ } catch (java.nio.file.ProviderNotFoundException e) {
+ output.append('fs-new-uri-map|')
+ }
+
+ try {
+ java.nio.file.FileSystems.newFileSystem(unsupportedUri, [:],
getClass().classLoader)
+ } catch (java.nio.file.ProviderNotFoundException e) {
+ output.append('fs-new-uri-map-loader|')
+ }
+
+ try {
+ java.nio.file.FileSystems.newFileSystem(path)
+ } catch (java.io.IOException | java.nio.file.ProviderNotFoundException e) {
+ output.append('fs-new-path|')
+ }
+
+ try {
+ java.nio.file.FileSystems.newFileSystem(path, getClass().classLoader)
+ } catch (java.io.IOException | java.nio.file.ProviderNotFoundException e) {
+ output.append('fs-new-path-loader|')
+ }
+
+ try {
+ java.nio.file.FileSystems.newFileSystem(path, [:])
+ } catch (java.io.IOException | java.nio.file.ProviderNotFoundException e) {
+ output.append('fs-new-path-map|')
+ }
+
+ try {
+ java.nio.file.FileSystems.newFileSystem(path, [:], getClass().classLoader)
+ } catch (java.io.IOException | java.nio.file.ProviderNotFoundException e) {
+ output.append('fs-new-path-map-loader|')
+ }
+
+ return output
+}
diff --git
a/core/spring/src/test/resources/FileSystemsReadStringMacroActions.groovy
b/core/spring/src/test/resources/FileSystemsReadStringMacroActions.groovy
new file mode 100644
index 0000000000..e7bf14ecbb
--- /dev/null
+++ b/core/spring/src/test/resources/FileSystemsReadStringMacroActions.groovy
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class FileSystemsReadStringMacroActions extends Script implements
MacroActions {}
+@BaseScript FileSystemsReadStringMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def path = java.nio.file.FileSystems.getDefault().getPath(output.toString())
+ output.append(': ').append(java.nio.file.Files.readString(path))
+ return output
+}
diff --git
a/core/spring/src/test/resources/FilesCreateTempDirectoryMacroActions.groovy
b/core/spring/src/test/resources/FilesCreateTempDirectoryMacroActions.groovy
new file mode 100644
index 0000000000..d95263b230
--- /dev/null
+++ b/core/spring/src/test/resources/FilesCreateTempDirectoryMacroActions.groovy
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class FilesCreateTempDirectoryMacroActions extends Script implements
MacroActions {}
+@BaseScript FilesCreateTempDirectoryMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def dir =
java.nio.file.Files.createTempDirectory('sandbox-files-create-temp-dir-')
+ output.append(dir.toAbsolutePath().toString())
+ return output
+}
diff --git
a/core/spring/src/test/resources/FilesCreateTempFileMacroActions.groovy
b/core/spring/src/test/resources/FilesCreateTempFileMacroActions.groovy
new file mode 100644
index 0000000000..c82355236f
--- /dev/null
+++ b/core/spring/src/test/resources/FilesCreateTempFileMacroActions.groovy
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class FilesCreateTempFileMacroActions extends Script implements
MacroActions {}
+@BaseScript FilesCreateTempFileMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def path = java.nio.file.Files.createTempFile('sandbox-files-create-temp-',
'.txt')
+ java.nio.file.Files.writeString(path, 'sandbox-files-create-temp-ok')
+ output.append(java.nio.file.Files.readString(path))
+ return output
+}
diff --git
a/core/spring/src/test/resources/GroovyClassLoaderOverloadsMacroActions.groovy
b/core/spring/src/test/resources/GroovyClassLoaderOverloadsMacroActions.groovy
new file mode 100644
index 0000000000..f797a37d0b
--- /dev/null
+++
b/core/spring/src/test/resources/GroovyClassLoaderOverloadsMacroActions.groovy
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class GroovyClassLoaderOverloadsMacroActions extends Script
implements MacroActions {}
+@BaseScript GroovyClassLoaderOverloadsMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def loader = new groovy.lang.GroovyClassLoader()
+
+ output.append(loader.parseClass(
+ 'class TmpGclOne { static String value() { "gcl-string|" } }').value())
+ output.append(loader.parseClass(
+ 'class TmpGclTwo { static String value() { "gcl-string-name|" } }',
+ 'TmpGclTwo.groovy').value())
+ output.append(loader.parseClass(
+ new java.io.StringReader('class TmpGclThree { static String value() {
"gcl-reader-name|" } }'),
+ 'TmpGclThree.groovy').value())
+
+ return output
+}
diff --git
a/core/spring/src/test/resources/GroovyShellOverloadsMacroActions.groovy
b/core/spring/src/test/resources/GroovyShellOverloadsMacroActions.groovy
new file mode 100644
index 0000000000..eed450af19
--- /dev/null
+++ b/core/spring/src/test/resources/GroovyShellOverloadsMacroActions.groovy
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class GroovyShellOverloadsMacroActions extends Script implements
MacroActions {}
+@BaseScript GroovyShellOverloadsMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def shell = new groovy.lang.GroovyShell()
+
+ output.append(shell.evaluate("'shell-evaluate-string|'"))
+ output.append(shell.evaluate("'shell-evaluate-string-name|'",
'TmpEvaluate.groovy'))
+ output.append(shell.evaluate("'shell-evaluate-string-name-codebase|'",
'TmpEvaluate.groovy', '/tmp'))
+ output.append(shell.evaluate(new
java.io.StringReader("'shell-evaluate-reader|'")))
+ output.append(shell.evaluate(new
java.io.StringReader("'shell-evaluate-reader-name|'"),
'TmpEvaluateReader.groovy'))
+ output.append(shell.evaluate(ctx.scriptFile as java.io.File))
+ output.append(shell.evaluate((ctx.scriptFile as java.io.File).toURI()))
+ output.append(shell.parse("return 'shell-parse-string|'").run())
+ output.append(shell.parse(new java.io.StringReader("return
'shell-parse-reader|'")).run())
+ output.append(shell.run("return 'shell-run-string-list|'", 'TmpRun.groovy',
[]))
+ output.append(shell.run(new java.io.StringReader("return
'shell-run-reader-list|'"), 'TmpRunReader.groovy', []))
+
+ return output
+}
diff --git
a/core/spring/src/test/resources/GroovyShellRuntimeExecMacroActions.groovy
b/core/spring/src/test/resources/GroovyShellRuntimeExecMacroActions.groovy
new file mode 100644
index 0000000000..0bf2aef7fb
--- /dev/null
+++ b/core/spring/src/test/resources/GroovyShellRuntimeExecMacroActions.groovy
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class GroovyShellRuntimeExecMacroActions extends Script implements
MacroActions {}
+@BaseScript GroovyShellRuntimeExecMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ output.append(new groovy.lang.GroovyShell().evaluate(
+ "java.lang.Runtime.getRuntime().exec(['/bin/sh','-c','printf
sandbox-groovy-shell-ok'] as String[]).inputStream.text"))
+ return output
+}
diff --git
a/core/spring/src/test/resources/MethodHandlesOverloadsMacroActions.groovy
b/core/spring/src/test/resources/MethodHandlesOverloadsMacroActions.groovy
new file mode 100644
index 0000000000..956d8c7583
--- /dev/null
+++ b/core/spring/src/test/resources/MethodHandlesOverloadsMacroActions.groovy
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class MethodHandlesOverloadsMacroActions extends Script implements
MacroActions {}
+@BaseScript MethodHandlesOverloadsMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def lookup = java.lang.invoke.MethodHandles.lookup()
+ def publicLookup = java.lang.invoke.MethodHandles.publicLookup()
+
+ def staticHandle = publicLookup.findStatic(
+ java.lang.Integer,
+ 'toString',
+ java.lang.invoke.MethodType.methodType(java.lang.String, int.class))
+ output.append(staticHandle.invokeWithArguments([7] as
java.util.List)).append('|')
+
+ def virtualHandle = publicLookup.findVirtual(
+ java.lang.String,
+ 'toUpperCase',
+ java.lang.invoke.MethodType.methodType(java.lang.String))
+ output.append(virtualHandle.invokeWithArguments(['mh-virtual'] as
java.util.List)).append('|')
+
+ def constructorHandle = publicLookup.findConstructor(
+ java.lang.String,
+ java.lang.invoke.MethodType.methodType(void.class, byte[].class))
+ output.append(constructorHandle.invokeWithArguments(['mh-constructor'.bytes]
as java.util.List)).append('|')
+
+ def boundHandle = publicLookup.bind(
+ 'mh-bind',
+ 'toUpperCase',
+ java.lang.invoke.MethodType.methodType(java.lang.String))
+ output.append(boundHandle.invokeWithArguments([] as Object[])).append('|')
+
+ output.append(java.lang.invoke.MethodHandles.reflectAs(
+ java.lang.reflect.Method,
+ staticHandle).name).append('|')
+
+ return output
+}
diff --git
a/core/spring/src/test/resources/MethodHandlesRuntimeExecMacroActions.groovy
b/core/spring/src/test/resources/MethodHandlesRuntimeExecMacroActions.groovy
new file mode 100644
index 0000000000..fffbb18f62
--- /dev/null
+++ b/core/spring/src/test/resources/MethodHandlesRuntimeExecMacroActions.groovy
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class MethodHandlesRuntimeExecMacroActions extends Script implements
MacroActions {}
+@BaseScript MethodHandlesRuntimeExecMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def lookup = java.lang.invoke.MethodHandles.publicLookup()
+ def getRuntime = lookup.findStatic(
+ java.lang.Runtime,
+ 'getRuntime',
+ java.lang.invoke.MethodType.methodType(java.lang.Runtime))
+ def runtime = getRuntime.invokeWithArguments([] as Object[])
+ def exec = lookup.findVirtual(
+ java.lang.Runtime,
+ 'exec',
+ java.lang.invoke.MethodType.methodType(java.lang.Process,
String[].class))
+ def process = exec.invokeWithArguments(
+ runtime,
+ ['/bin/sh', '-c', 'printf sandbox-method-handles-ok'] as String[])
+ output.append(process.inputStream.text)
+ return output
+}
diff --git
a/core/spring/src/test/resources/PathOfFilesReadStringMacroActions.groovy
b/core/spring/src/test/resources/PathOfFilesReadStringMacroActions.groovy
new file mode 100644
index 0000000000..f7d41e5d21
--- /dev/null
+++ b/core/spring/src/test/resources/PathOfFilesReadStringMacroActions.groovy
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class PathOfFilesReadStringMacroActions extends Script implements
MacroActions {}
+@BaseScript PathOfFilesReadStringMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ String path = output.toString()
+ output.append(':
').append(java.nio.file.Files.readString(java.nio.file.Path.of(path)))
+ return output
+}
diff --git
a/core/spring/src/test/resources/PathOfFilesWriteStringMacroActions.groovy
b/core/spring/src/test/resources/PathOfFilesWriteStringMacroActions.groovy
new file mode 100644
index 0000000000..2395616a6e
--- /dev/null
+++ b/core/spring/src/test/resources/PathOfFilesWriteStringMacroActions.groovy
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class PathOfFilesWriteStringMacroActions extends Script implements
MacroActions {}
+@BaseScript PathOfFilesWriteStringMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ java.nio.file.Files.writeString(java.nio.file.Path.of(output.toString()),
'sandbox-write-ok')
+ return output
+}
diff --git
a/core/spring/src/test/resources/PathOfUriFilesReadStringMacroActions.groovy
b/core/spring/src/test/resources/PathOfUriFilesReadStringMacroActions.groovy
new file mode 100644
index 0000000000..a45adc1ec7
--- /dev/null
+++ b/core/spring/src/test/resources/PathOfUriFilesReadStringMacroActions.groovy
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class PathOfUriFilesReadStringMacroActions extends Script implements
MacroActions {}
+@BaseScript PathOfUriFilesReadStringMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def path = java.nio.file.Path.of(java.net.URI.create(output.toString()))
+ output.append(': ').append(java.nio.file.Files.readString(path))
+ return output
+}
diff --git
a/core/spring/src/test/resources/PathsGetFilesReadStringMacroActions.groovy
b/core/spring/src/test/resources/PathsGetFilesReadStringMacroActions.groovy
new file mode 100644
index 0000000000..e0db1fb3da
--- /dev/null
+++ b/core/spring/src/test/resources/PathsGetFilesReadStringMacroActions.groovy
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class PathsGetFilesReadStringMacroActions extends Script implements
MacroActions {}
+@BaseScript PathsGetFilesReadStringMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def path = java.nio.file.Paths.get(output.toString())
+ output.append(': ').append(java.nio.file.Files.readString(path))
+ return output
+}
diff --git
a/core/spring/src/test/resources/PathsGetUriFilesReadStringMacroActions.groovy
b/core/spring/src/test/resources/PathsGetUriFilesReadStringMacroActions.groovy
new file mode 100644
index 0000000000..a27bab09dc
--- /dev/null
+++
b/core/spring/src/test/resources/PathsGetUriFilesReadStringMacroActions.groovy
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class PathsGetUriFilesReadStringMacroActions extends Script
implements MacroActions {}
+@BaseScript PathsGetUriFilesReadStringMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def path = java.nio.file.Paths.get(java.net.URI.create(output.toString()))
+ output.append(': ').append(java.nio.file.Files.readString(path))
+ return output
+}
diff --git
a/core/spring/src/test/resources/ProcessBuilderStartPipelineMacroActions.groovy
b/core/spring/src/test/resources/ProcessBuilderStartPipelineMacroActions.groovy
new file mode 100644
index 0000000000..14be68b400
--- /dev/null
+++
b/core/spring/src/test/resources/ProcessBuilderStartPipelineMacroActions.groovy
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class ProcessBuilderStartPipelineMacroActions extends Script
implements MacroActions {}
+@BaseScript ProcessBuilderStartPipelineMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def processes = java.lang.ProcessBuilder.startPipeline(ctx.builders)
+ output.append(processes.last().inputStream.text)
+ return output
+}
diff --git a/core/spring/src/test/resources/RuntimeExecMacroActions.groovy
b/core/spring/src/test/resources/RuntimeExecMacroActions.groovy
new file mode 100644
index 0000000000..b9e5d1eb51
--- /dev/null
+++ b/core/spring/src/test/resources/RuntimeExecMacroActions.groovy
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class RuntimeExecMacroActions extends Script implements MacroActions
{}
+@BaseScript RuntimeExecMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def process = ctx.runtime.exec(['/bin/sh', '-c', 'printf
sandbox-runtime-exec-ok'] as String[])
+ output.append(process.inputStream.text)
+ return output
+}
diff --git
a/core/spring/src/test/resources/RuntimeExecOverloadsMacroActions.groovy
b/core/spring/src/test/resources/RuntimeExecOverloadsMacroActions.groovy
new file mode 100644
index 0000000000..a57a61a474
--- /dev/null
+++ b/core/spring/src/test/resources/RuntimeExecOverloadsMacroActions.groovy
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class RuntimeExecOverloadsMacroActions extends Script implements
MacroActions {}
+@BaseScript RuntimeExecOverloadsMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def runtime = ctx.runtime as java.lang.Runtime
+ def append = { Process process ->
+ process.waitFor()
+ output.append(process.inputStream.text)
+ }
+
+ append(runtime.exec('/bin/printf runtime-string|'))
+ append(runtime.exec('/bin/printf runtime-string-env|', null as String[]))
+ append(runtime.exec('/bin/printf runtime-string-env-dir|', null as String[],
null as java.io.File))
+ append(runtime.exec(['/bin/sh', '-c', "printf 'runtime-array|'"] as
String[]))
+ append(runtime.exec(['/bin/sh', '-c', "printf 'runtime-array-env|'"] as
String[], null as String[]))
+ append(runtime.exec(['/bin/sh', '-c', "printf 'runtime-array-env-dir|'"] as
String[], null as String[], null as java.io.File))
+
+ return output
+}
diff --git
a/core/spring/src/test/resources/ScriptEngineOverloadsMacroActions.groovy
b/core/spring/src/test/resources/ScriptEngineOverloadsMacroActions.groovy
new file mode 100644
index 0000000000..2bde95c36d
--- /dev/null
+++ b/core/spring/src/test/resources/ScriptEngineOverloadsMacroActions.groovy
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+import groovy.transform.BaseScript
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+import java.io.Serializable
+
+abstract class ScriptEngineOverloadsMacroActions extends Script implements
MacroActions {}
+@BaseScript ScriptEngineOverloadsMacroActions _
+
+@Override
+StringBuilder afterAll(Map<String, Serializable> ctx, StringBuilder output) {
+ def manager = new javax.script.ScriptEngineManager()
+ def engine = manager.getEngineByName('groovy')
+
+ output.append(engine.eval("'script-engine-string|'"))
+ output.append(engine.eval(new
java.io.StringReader("'script-engine-reader|'")))
+
output.append(manager.getEngineByExtension('groovy').eval("'script-engine-extension|'"))
+
+ return output
+}