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 5a6a25b6cd Strengthen Groovy sandbox
5a6a25b6cd is described below

commit 5a6a25b6cd44268137d1c4e2472fc9a0480d10e9
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Tue Oct 20 16:39:14 2026 +0200

    Strengthen Groovy sandbox
---
 .../src/main/resources/META-INF/groovy.blacklist   |  10 +
 .../spring/implementation/GroovySandboxTest.java   | 218 +++++++++++----------
 ...ProcessGroovyMethodsSchedTaskJobDelegate.groovy |  30 +++
 .../ThreadLocalBypassSchedTaskJobDelegate.groovy   |  47 +++++
 4 files changed, 206 insertions(+), 99 deletions(-)

diff --git a/core/spring/src/main/resources/META-INF/groovy.blacklist 
b/core/spring/src/main/resources/META-INF/groovy.blacklist
index cfe5dfeccd..4d22b3f751 100644
--- a/core/spring/src/main/resources/META-INF/groovy.blacklist
+++ b/core/spring/src/main/resources/META-INF/groovy.blacklist
@@ -250,3 +250,13 @@ staticMethod 
org.codehaus.groovy.runtime.ScriptBytecodeAdapter castToType java.l
 # SECURITY-1754
 new org.kohsuke.groovy.sandbox.impl.Checker$SuperConstructorWrapper 
java.lang.Object[]
 new org.kohsuke.groovy.sandbox.impl.Checker$ThisConstructorWrapper 
java.lang.Object[]
+
+staticMethod org.codehaus.groovy.runtime.ProcessGroovyMethods execute 
java.lang.String
+staticMethod org.codehaus.groovy.runtime.ProcessGroovyMethods execute 
java.lang.String java.lang.String[] java.io.File
+staticMethod org.codehaus.groovy.runtime.ProcessGroovyMethods execute 
java.lang.String[]
+staticMethod org.codehaus.groovy.runtime.ProcessGroovyMethods execute 
java.lang.String[] java.lang.String[] java.io.File
+staticMethod org.codehaus.groovy.runtime.ProcessGroovyMethods execute 
java.util.List
+staticMethod org.codehaus.groovy.runtime.ProcessGroovyMethods execute 
java.util.List java.lang.String[] java.io.File
+staticMethod org.codehaus.groovy.runtime.ProcessGroovyMethods execute 
java.lang.Runtime java.lang.String
+staticMethod org.codehaus.groovy.runtime.ProcessGroovyMethods execute 
java.lang.Runtime java.lang.String java.lang.String[] java.io.File
+staticMethod org.codehaus.groovy.runtime.ProcessGroovyMethods execute 
java.lang.Runtime 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 81a3393217..4813c011e3 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
@@ -34,7 +34,10 @@ import java.util.Objects;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.types.ImplementationEngine;
+import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
+import org.apache.syncope.core.provisioning.api.job.JobExecutionException;
+import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
 import org.apache.syncope.core.provisioning.api.macro.MacroActions;
 import org.apache.syncope.core.spring.SpringTestConfiguration;
 import org.identityconnectors.common.logging.Log;
@@ -51,6 +54,17 @@ import 
org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
 @SpringJUnitConfig(classes = { SpringTestConfiguration.class })
 class GroovySandboxTest {
 
+    private static <T> T build(final String key, final String resource) throws 
Exception {
+        Implementation impl = mock(Implementation.class);
+        when(impl.getKey()).thenReturn(key);
+        when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY);
+        when(impl.getBody()).thenReturn(new String(
+                
Objects.requireNonNull(GroovySandboxTest.class.getResourceAsStream(resource)).readAllBytes(),
+                StandardCharsets.UTF_8));
+
+        return ImplementationManager.build(SyncopeConstants.MASTER_DOMAIN, 
impl);
+    }
+
     @TempDir
     private Path tempDir;
 
@@ -73,52 +87,40 @@ class GroovySandboxTest {
 
     @Test
     void processBuilder() throws Exception {
-        final MacroActions actions = actions("processBuilder", 
"/ProcessBuilderMacroActions.groovy");
+        MacroActions actions = build("processBuilder", 
"/ProcessBuilderMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class, () -> actions.afterAll(null, new 
StringBuilder()));
         assertTrue(e.getMessage().contains("Insecure call to 'new 
java.lang.ProcessBuilder java.lang.String[]'"));
     }
 
-    private static MacroActions actions(final String key, final String 
resource) throws Exception {
-        Implementation impl = mock(Implementation.class);
-        when(impl.getKey()).thenReturn(key);
-        when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY);
-        when(impl.getBody()).thenReturn(new String(
-                
Objects.requireNonNull(GroovySandboxTest.class.getResourceAsStream(resource)).readAllBytes(),
-                StandardCharsets.UTF_8));
-
-        return ImplementationManager.build(SyncopeConstants.MASTER_DOMAIN, 
impl);
-    }
-
     @EnabledOnOs(OS.LINUX)
     @Test
     void bash() throws Exception {
-        final MacroActions actions = actions("bash", 
"/BashMacroActions.groovy");
+        MacroActions actions = build("bash", "/BashMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        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 {
-        final BeanCreationException e =
-                assertThrows(
-                        BeanCreationException.class,
-                        () -> actions("staticMacroActions", 
"/StaticMacroActions.groovy"));
-        final SecurityException sec = (SecurityException) 
ExceptionUtils.getRootCause(e);
+        BeanCreationException e = assertThrows(
+                BeanCreationException.class,
+                () -> build("staticMacroActions", 
"/StaticMacroActions.groovy"));
+        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");
+        Path testFile = tempDir.resolve("sandbox-read.txt");
         Files.writeString(testFile, "sandbox-read-ok");
 
-        final MacroActions actions = actions("pathOfFilesReadString", 
"/PathOfFilesReadStringMacroActions.groovy");
+        MacroActions actions = build("pathOfFilesReadString", 
"/PathOfFilesReadStringMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new 
StringBuilder(testFile.toAbsolutePath().toString())));
         assertTrue(e.getMessage().contains(
@@ -127,11 +129,11 @@ class GroovySandboxTest {
 
     @Test
     void pathOfFilesWriteString() throws Exception {
-        final Path testFile = tempDir.resolve("sandbox-write.txt");
+        Path testFile = tempDir.resolve("sandbox-write.txt");
 
-        final MacroActions actions = actions("pathOfFilesWriteString", 
"/PathOfFilesWriteStringMacroActions.groovy");
+        MacroActions actions = build("pathOfFilesWriteString", 
"/PathOfFilesWriteStringMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new 
StringBuilder(testFile.toAbsolutePath().toString())));
         assertTrue(e.getMessage().contains(
@@ -140,13 +142,12 @@ class GroovySandboxTest {
 
     @Test
     void pathOfUriFilesReadString() throws Exception {
-        final Path testFile = tempDir.resolve("sandbox-read-uri.txt");
+        Path testFile = tempDir.resolve("sandbox-read-uri.txt");
         Files.writeString(testFile, "sandbox-read-uri-ok");
 
-        final MacroActions actions =
-                actions("pathOfUriFilesReadString", 
"/PathOfUriFilesReadStringMacroActions.groovy");
+        MacroActions actions = build("pathOfUriFilesReadString", 
"/PathOfUriFilesReadStringMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new 
StringBuilder(testFile.toUri().toString())));
         assertTrue(e.getMessage().contains(
@@ -155,12 +156,12 @@ class GroovySandboxTest {
 
     @Test
     void pathsGetFilesReadString() throws Exception {
-        final Path testFile = tempDir.resolve("sandbox-paths-get-read.txt");
+        Path testFile = tempDir.resolve("sandbox-paths-get-read.txt");
         Files.writeString(testFile, "sandbox-paths-get-read-ok");
 
-        final MacroActions actions = actions("pathsGetFilesReadString", 
"/PathsGetFilesReadStringMacroActions.groovy");
+        MacroActions actions = build("pathsGetFilesReadString", 
"/PathsGetFilesReadStringMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new 
StringBuilder(testFile.toAbsolutePath().toString())));
         assertTrue(e.getMessage().contains(
@@ -169,13 +170,12 @@ class GroovySandboxTest {
 
     @Test
     void pathsGetUriFilesReadString() throws Exception {
-        final Path testFile = 
tempDir.resolve("sandbox-paths-get-uri-read.txt");
+        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");
+        MacroActions actions = build("pathsGetUriFilesReadString", 
"/PathsGetUriFilesReadStringMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new 
StringBuilder(testFile.toUri().toString())));
         assertTrue(e.getMessage().contains(
@@ -184,12 +184,12 @@ class GroovySandboxTest {
 
     @Test
     void fileSystemsReadString() throws Exception {
-        final Path testFile = tempDir.resolve("sandbox-filesystems-read.txt");
+        Path testFile = tempDir.resolve("sandbox-filesystems-read.txt");
         Files.writeString(testFile, "sandbox-filesystems-read-ok");
 
-        final MacroActions actions = actions("fileSystemsReadString", 
"/FileSystemsReadStringMacroActions.groovy");
+        MacroActions actions = build("fileSystemsReadString", 
"/FileSystemsReadStringMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new 
StringBuilder(testFile.toAbsolutePath().toString())));
         assertTrue(e.getMessage().contains(
@@ -198,13 +198,13 @@ class GroovySandboxTest {
 
     @Test
     void fileSystemProviderReadString() throws Exception {
-        final Path testFile = 
tempDir.resolve("sandbox-filesystem-provider-read.txt");
+        Path testFile = 
tempDir.resolve("sandbox-filesystem-provider-read.txt");
         Files.writeString(testFile, "sandbox-filesystem-provider-read-ok");
 
-        final MacroActions actions =
-                actions("fileSystemProviderReadString", 
"/FileSystemProviderReadStringMacroActions.groovy");
+        MacroActions actions =
+                build("fileSystemProviderReadString", 
"/FileSystemProviderReadStringMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new 
StringBuilder(testFile.toUri().toString())));
         assertTrue(e.getMessage().contains(
@@ -213,9 +213,9 @@ class GroovySandboxTest {
 
     @Test
     void filesCreateTempFile() throws Exception {
-        final MacroActions actions = actions("filesCreateTempFile", 
"/FilesCreateTempFileMacroActions.groovy");
+        MacroActions actions = build("filesCreateTempFile", 
"/FilesCreateTempFileMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new StringBuilder()));
         assertTrue(e.getMessage().contains(
@@ -225,10 +225,9 @@ class GroovySandboxTest {
 
     @Test
     void filesCreateTempDirectory() throws Exception {
-        final MacroActions actions =
-                actions("filesCreateTempDirectory", 
"/FilesCreateTempDirectoryMacroActions.groovy");
+        MacroActions actions = build("filesCreateTempDirectory", 
"/FilesCreateTempDirectoryMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new StringBuilder()));
         assertTrue(e.getMessage().contains(
@@ -239,16 +238,14 @@ class GroovySandboxTest {
     @EnabledOnOs(OS.LINUX)
     @Test
     void processBuilderStartPipeline() throws Exception {
-        final MacroActions actions =
-                actions("processBuilderStartPipeline", 
"/ProcessBuilderStartPipelineMacroActions.groovy");
+        MacroActions actions = build("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")));
+        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 e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(ctx, new StringBuilder()));
         assertTrue(e.getMessage().contains(
@@ -258,13 +255,13 @@ class GroovySandboxTest {
     @EnabledOnOs(OS.LINUX)
     @Test
     void runtimeExec() throws Exception {
-        final MacroActions actions = actions("runtimeExec", 
"/RuntimeExecMacroActions.groovy");
+        MacroActions actions = build("runtimeExec", 
"/RuntimeExecMacroActions.groovy");
 
         @SuppressWarnings("unchecked")
-        final Map<String, java.io.Serializable> ctx =
+        Map<String, java.io.Serializable> ctx =
                 (Map<String, java.io.Serializable>) (Map<?, ?>) 
Map.of("runtime", Runtime.getRuntime());
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(ctx, new StringBuilder()));
         assertTrue(e.getMessage().contains(
@@ -274,10 +271,9 @@ class GroovySandboxTest {
     @EnabledOnOs(OS.LINUX)
     @Test
     void beansExpressionRuntimeExec() throws Exception {
-        final MacroActions actions =
-                actions("beansExpressionRuntimeExec", 
"/BeansExpressionRuntimeExecMacroActions.groovy");
+        MacroActions actions = build("beansExpressionRuntimeExec", 
"/BeansExpressionRuntimeExecMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new StringBuilder()));
         assertTrue(e.getMessage().contains(
@@ -286,10 +282,9 @@ class GroovySandboxTest {
 
     @Test
     void beansStatementSystemExit() throws Exception {
-        final MacroActions actions =
-                actions("beansStatementSystemExit", 
"/BeansStatementSystemExitMacroActions.groovy");
+        MacroActions actions = build("beansStatementSystemExit", 
"/BeansStatementSystemExitMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new StringBuilder()));
         assertTrue(e.getMessage().contains(
@@ -299,9 +294,9 @@ class GroovySandboxTest {
     @EnabledOnOs(OS.LINUX)
     @Test
     void groovyShellRuntimeExec() throws Exception {
-        final MacroActions actions = actions("groovyShellRuntimeExec", 
"/GroovyShellRuntimeExecMacroActions.groovy");
+        MacroActions actions = build("groovyShellRuntimeExec", 
"/GroovyShellRuntimeExecMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new StringBuilder()));
         assertTrue(e.getMessage().contains("Insecure call to 'new 
groovy.lang.GroovyShell'"));
@@ -310,9 +305,9 @@ class GroovySandboxTest {
     @EnabledOnOs(OS.LINUX)
     @Test
     void evalRuntimeExec() throws Exception {
-        final MacroActions actions = actions("evalRuntimeExec", 
"/EvalRuntimeExecMacroActions.groovy");
+        MacroActions actions = build("evalRuntimeExec", 
"/EvalRuntimeExecMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new StringBuilder()));
         assertTrue(e.getMessage().contains(
@@ -322,10 +317,9 @@ class GroovySandboxTest {
     @EnabledOnOs(OS.LINUX)
     @Test
     void methodHandlesRuntimeExec() throws Exception {
-        final MacroActions actions =
-                actions("methodHandlesRuntimeExec", 
"/MethodHandlesRuntimeExecMacroActions.groovy");
+        MacroActions actions = build("methodHandlesRuntimeExec", 
"/MethodHandlesRuntimeExecMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new StringBuilder()));
         assertTrue(e.getMessage().contains(
@@ -335,13 +329,13 @@ class GroovySandboxTest {
     @EnabledOnOs(OS.LINUX)
     @Test
     void runtimeExecOverloads() throws Exception {
-        final MacroActions actions = actions("runtimeExecOverloads", 
"/RuntimeExecOverloadsMacroActions.groovy");
+        MacroActions actions = build("runtimeExecOverloads", 
"/RuntimeExecOverloadsMacroActions.groovy");
 
         @SuppressWarnings("unchecked")
-        final Map<String, java.io.Serializable> ctx =
+        Map<String, java.io.Serializable> ctx =
                 (Map<String, java.io.Serializable>) (Map<?, ?>) 
Map.of("runtime", Runtime.getRuntime());
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(ctx, new StringBuilder()));
         assertTrue(e.getMessage().contains(
@@ -350,9 +344,9 @@ class GroovySandboxTest {
 
     @Test
     void evalOverloads() throws Exception {
-        final MacroActions actions = actions("evalOverloads", 
"/EvalOverloadsMacroActions.groovy");
+        MacroActions actions = build("evalOverloads", 
"/EvalOverloadsMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new StringBuilder()));
         assertTrue(e.getMessage().contains(
@@ -361,15 +355,15 @@ class GroovySandboxTest {
 
     @Test
     void groovyShellOverloads() throws Exception {
-        final Path script = 
tempDir.resolve("sandbox-groovy-shell-overload.groovy");
+        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> ctx =
                 (Map<String, java.io.Serializable>) (Map<?, ?>) 
Map.of("scriptFile", script.toFile());
 
-        final MacroActions actions = actions("groovyShellOverloads", 
"/GroovyShellOverloadsMacroActions.groovy");
+        MacroActions actions = build("groovyShellOverloads", 
"/GroovyShellOverloadsMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(ctx, new StringBuilder()));
         assertTrue(e.getMessage().contains("Insecure call to 'new 
groovy.lang.GroovyShell'"));
@@ -377,10 +371,10 @@ class GroovySandboxTest {
 
     @Test
     void groovyClassLoaderOverloads() throws Exception {
-        final MacroActions actions =
-                actions("groovyClassLoaderOverloads", 
"/GroovyClassLoaderOverloadsMacroActions.groovy");
+        MacroActions actions = build(
+                "groovyClassLoaderOverloads", 
"/GroovyClassLoaderOverloadsMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new StringBuilder()));
         assertTrue(e.getMessage().contains("Insecure call to 'new 
groovy.lang.GroovyClassLoader'"));
@@ -388,9 +382,9 @@ class GroovySandboxTest {
 
     @Test
     void scriptEngineOverloads() throws Exception {
-        final MacroActions actions = actions("scriptEngineOverloads", 
"/ScriptEngineOverloadsMacroActions.groovy");
+        MacroActions actions = build("scriptEngineOverloads", 
"/ScriptEngineOverloadsMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new StringBuilder()));
         assertTrue(e.getMessage().contains("Insecure call to 'new 
javax.script.ScriptEngineManager'"));
@@ -398,15 +392,15 @@ class GroovySandboxTest {
 
     @Test
     void fileSystemsOverloads() throws Exception {
-        final Path file = tempDir.resolve("sandbox-filesystems-overloads.txt");
+        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> ctx =
                 (Map<String, java.io.Serializable>) (Map<?, ?>) Map.of("path", 
file);
 
-        final MacroActions actions = actions("fileSystemsOverloads", 
"/FileSystemsOverloadsMacroActions.groovy");
+        MacroActions actions = build("fileSystemsOverloads", 
"/FileSystemsOverloadsMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(ctx, new StringBuilder()));
         assertTrue(e.getMessage().contains(
@@ -415,18 +409,17 @@ class GroovySandboxTest {
 
     @Test
     void fileSystemProviderOverloads() throws Exception {
-        final Path file = 
tempDir.resolve("sandbox-filesystem-provider-overloads.txt");
+        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> ctx =
                 (Map<String, java.io.Serializable>) (Map<?, ?>) Map.of(
                         "path", file,
                         "provider", file.getFileSystem().provider());
 
-        final MacroActions actions =
-                actions("fileSystemProviderOverloads", 
"/FileSystemProviderOverloadsMacroActions.groovy");
+        MacroActions actions = build("fileSystemProviderOverloads", 
"/FileSystemProviderOverloadsMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(ctx, new StringBuilder()));
         assertTrue(e.getMessage().contains(
@@ -435,9 +428,9 @@ class GroovySandboxTest {
 
     @Test
     void methodHandlesOverloads() throws Exception {
-        final MacroActions actions = actions("methodHandlesOverloads", 
"/MethodHandlesOverloadsMacroActions.groovy");
+        MacroActions actions = build("methodHandlesOverloads", 
"/MethodHandlesOverloadsMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        SecurityException e = assertThrows(
                 SecurityException.class,
                 () -> actions.afterAll(null, new StringBuilder()));
         assertTrue(e.getMessage().contains(
@@ -446,12 +439,39 @@ class GroovySandboxTest {
 
     @Test
     void beansOverloads() throws Exception {
-        final MacroActions actions = actions("beansOverloads", 
"/BeansOverloadsMacroActions.groovy");
+        MacroActions actions = build("beansOverloads", 
"/BeansOverloadsMacroActions.groovy");
 
-        final SecurityException e = assertThrows(
+        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[]'"));
     }
+
+    @EnabledOnOs(OS.LINUX)
+    @Test
+    void processGroovyMethods() throws Exception {
+        SchedTaskJobDelegate delegate = build(
+                "processGroovyMethods", 
"/ProcessGroovyMethodsSchedTaskJobDelegate.groovy");
+
+        SecurityException e = assertThrows(
+                SecurityException.class,
+                () -> delegate.execute(TaskType.SCHEDULED, null, null));
+        assertTrue(e.getMessage().contains(
+                "Insecure call to 'staticMethod 
org.codehaus.groovy.runtime.ProcessGroovyMethods "
+                + "execute java.lang.String'"));
+    }
+
+    @EnabledOnOs(OS.LINUX)
+    @Test
+    void threadLocalBypass() throws Exception {
+        SchedTaskJobDelegate delegate = build("threadLocalBypass", 
"/ThreadLocalBypassSchedTaskJobDelegate.groovy");
+
+        JobExecutionException e = assertThrows(
+                JobExecutionException.class,
+                () -> delegate.execute(TaskType.SCHEDULED, null, null));
+        assertTrue(e.getMessage().contains(
+                "Insecure call to 'staticMethod 
org.codehaus.groovy.runtime.ProcessGroovyMethods "
+                + "execute java.lang.String'"));
+    }
 }
diff --git 
a/core/spring/src/test/resources/ProcessGroovyMethodsSchedTaskJobDelegate.groovy
 
b/core/spring/src/test/resources/ProcessGroovyMethodsSchedTaskJobDelegate.groovy
new file mode 100644
index 0000000000..bd9021b1ae
--- /dev/null
+++ 
b/core/spring/src/test/resources/ProcessGroovyMethodsSchedTaskJobDelegate.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 org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate
+import org.apache.syncope.core.provisioning.api.job.JobExecutionContext
+import org.apache.syncope.core.provisioning.api.job.JobExecutionException
+import org.apache.syncope.common.lib.types.TaskType
+
+class ProcessGroovyMethodsSchedTaskJobDelegate implements SchedTaskJobDelegate 
{
+
+  void execute(TaskType taskType, String taskKey, JobExecutionContext context) 
throws JobExecutionException {        
+    def output = "id".execute().text
+    throw new JobExecutionException("RCE_OUTPUT: " + output)
+  }
+}
diff --git 
a/core/spring/src/test/resources/ThreadLocalBypassSchedTaskJobDelegate.groovy 
b/core/spring/src/test/resources/ThreadLocalBypassSchedTaskJobDelegate.groovy
new file mode 100644
index 0000000000..188bd4bf53
--- /dev/null
+++ 
b/core/spring/src/test/resources/ThreadLocalBypassSchedTaskJobDelegate.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 org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate
+import org.apache.syncope.core.provisioning.api.job.JobExecutionContext
+import org.apache.syncope.core.provisioning.api.job.JobExecutionException
+import org.apache.syncope.common.lib.types.TaskType
+import java.util.concurrent.atomic.AtomicReference
+
+class ThreadLocalBypassSchedTaskJobDelegate implements SchedTaskJobDelegate {
+  
+  void execute(TaskType taskType, String taskKey, JobExecutionContext context) 
throws JobExecutionException {
+    def result = new AtomicReference("NOT_SET")
+    def runnable = new RceRunner(result)
+    def t = new Thread((Runnable) runnable)
+    t.start()
+    t.join(5000)
+    throw new JobExecutionException("THREAD_RCE: " + result.get())
+  }
+}
+
+class RceRunner implements Runnable {
+  AtomicReference result
+  RceRunner(AtomicReference r) { this.result = r }
+  void run() {
+    try {
+      result.set("whoami".execute().text)
+    } catch (Exception e) {
+      result.set("ERROR: " + e.getMessage())
+    }
+  }
+} 

Reply via email to