From 2a06125aab9e10cfd2bfa4d4cf18e70f3b0c6bcf Mon Sep 17 00:00:00 2001 From: Massimiliano Perrone Date: Wed, 27 May 2026 16:54:05 +0200 Subject: [PATCH 1/7] Add Path.of to Groovy sandbox blacklist --- .../main/resources/META-INF/groovy.blacklist | 2 + .../implementation/GroovySandboxTest.java | 51 +++++++++++++++++++ .../PathOfFilesReadStringMacroActions.groovy | 31 +++++++++++ .../PathOfFilesWriteStringMacroActions.groovy | 30 +++++++++++ 4 files changed, 114 insertions(+) create mode 100644 core/spring/src/test/resources/PathOfFilesReadStringMacroActions.groovy create mode 100644 core/spring/src/test/resources/PathOfFilesWriteStringMacroActions.groovy diff --git a/core/spring/src/main/resources/META-INF/groovy.blacklist b/core/spring/src/main/resources/META-INF/groovy.blacklist index 85292bc6085..d4f7f5b7bba 100644 --- a/core/spring/src/main/resources/META-INF/groovy.blacklist +++ b/core/spring/src/main/resources/META-INF/groovy.blacklist @@ -83,6 +83,8 @@ 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 # 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 8293b3c696d..0453cd462c4 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,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.nio.file.Files; +import java.nio.file.Path; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.cxf.helpers.IOUtils; import org.apache.syncope.common.lib.types.ImplementationEngine; @@ -32,12 +34,20 @@ 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.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.BeanCreationException; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; @SpringJUnitConfig(classes = { SpringTestConfiguration.class }) class GroovySandboxTest { + private static final Logger LOG = LoggerFactory.getLogger(GroovySandboxTest.class); + + @TempDir + private Path tempDir; + @Test void processBuilder() throws Exception { Implementation impl = mock(Implementation.class); @@ -81,4 +91,45 @@ void staticMacroActions() throws Exception { 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"); + LOG.info("pathOfFilesReadString: Created sandbox test file {} with content: {}", + testFile, Files.readString(testFile)); + + final Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("pathOfFilesReadString"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + getClass().getResourceAsStream("/PathOfFilesReadStringMacroActions.groovy"))); + + final MacroActions actions = ImplementationManager.build(impl); + + 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"); + + Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("pathOfFilesWriteString"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + getClass().getResourceAsStream("/PathOfFilesWriteStringMacroActions.groovy"))); + + final MacroActions actions = ImplementationManager.build(impl); + + 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[]'")); + } } diff --git a/core/spring/src/test/resources/PathOfFilesReadStringMacroActions.groovy b/core/spring/src/test/resources/PathOfFilesReadStringMacroActions.groovy new file mode 100644 index 00000000000..f7d41e5d211 --- /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 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 00000000000..2395616a6e6 --- /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 ctx, StringBuilder output) { + java.nio.file.Files.writeString(java.nio.file.Path.of(output.toString()), 'sandbox-write-ok') + return output +} From 40788ed9425bd131658b538b5673f5a9054bde04 Mon Sep 17 00:00:00 2001 From: Massimiliano Perrone Date: Wed, 27 May 2026 16:58:43 +0200 Subject: [PATCH 2/7] Removed LOGGER from Test class --- .../syncope/core/spring/implementation/GroovySandboxTest.java | 4 ---- 1 file changed, 4 deletions(-) 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 0453cd462c4..8b2fe2b99a6 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 @@ -43,8 +43,6 @@ @SpringJUnitConfig(classes = { SpringTestConfiguration.class }) class GroovySandboxTest { - private static final Logger LOG = LoggerFactory.getLogger(GroovySandboxTest.class); - @TempDir private Path tempDir; @@ -96,8 +94,6 @@ void staticMacroActions() throws Exception { void pathOfFilesReadString() throws Exception { final Path testFile = tempDir.resolve("sandbox-read.txt"); Files.writeString(testFile, "sandbox-read-ok"); - LOG.info("pathOfFilesReadString: Created sandbox test file {} with content: {}", - testFile, Files.readString(testFile)); final Implementation impl = mock(Implementation.class); when(impl.getKey()).thenReturn("pathOfFilesReadString"); From 9557a7f9fc4ce532047a0ab30374e5a2a3081c3f Mon Sep 17 00:00:00 2001 From: Massimiliano Perrone Date: Wed, 27 May 2026 16:59:26 +0200 Subject: [PATCH 3/7] Removed useless import --- .../syncope/core/spring/implementation/GroovySandboxTest.java | 2 -- 1 file changed, 2 deletions(-) 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 8b2fe2b99a6..bf5889255fd 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 @@ -35,8 +35,6 @@ import org.junit.jupiter.api.condition.EnabledOnOs; import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.io.TempDir; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.BeanCreationException; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; From 688f5829661dce9f07c54aac130cded7c84ba29c Mon Sep 17 00:00:00 2001 From: Massimiliano Perrone Date: Wed, 27 May 2026 17:02:38 +0200 Subject: [PATCH 4/7] added Objects.requireNonNull) --- .../spring/implementation/GroovySandboxTest.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) 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 bf5889255fd..09c74d0f3f1 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 @@ -25,6 +25,8 @@ import java.nio.file.Files; import java.nio.file.Path; +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; @@ -50,7 +52,7 @@ void processBuilder() throws Exception { when(impl.getKey()).thenReturn("processBuilder"); when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); when(impl.getBody()).thenReturn(IOUtils.toString( - getClass().getResourceAsStream("/ProcessBuilderMacroActions.groovy"))); + Objects.requireNonNull(getClass().getResourceAsStream("/ProcessBuilderMacroActions.groovy")))); MacroActions actions = ImplementationManager.build(impl); @@ -66,7 +68,7 @@ void bash() throws Exception { when(impl.getKey()).thenReturn("bash"); when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); when(impl.getBody()).thenReturn(IOUtils.toString( - getClass().getResourceAsStream("/BashMacroActions.groovy"))); + Objects.requireNonNull(getClass().getResourceAsStream("/BashMacroActions.groovy")))); MacroActions actions = ImplementationManager.build(impl); @@ -81,7 +83,7 @@ void staticMacroActions() throws Exception { when(impl.getKey()).thenReturn("staticMacroActions"); when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); when(impl.getBody()).thenReturn(IOUtils.toString( - getClass().getResourceAsStream("/StaticMacroActions.groovy"))); + Objects.requireNonNull(getClass().getResourceAsStream("/StaticMacroActions.groovy")))); BeanCreationException e = assertThrows(BeanCreationException.class, () -> ImplementationManager.build(impl)); SecurityException sec = (SecurityException) ExceptionUtils.getRootCause(e); @@ -97,7 +99,8 @@ void pathOfFilesReadString() throws Exception { when(impl.getKey()).thenReturn("pathOfFilesReadString"); when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); when(impl.getBody()).thenReturn(IOUtils.toString( - getClass().getResourceAsStream("/PathOfFilesReadStringMacroActions.groovy"))); + Objects.requireNonNull(getClass().getResourceAsStream( + "/PathOfFilesReadStringMacroActions.groovy")))); final MacroActions actions = ImplementationManager.build(impl); @@ -116,7 +119,8 @@ void pathOfFilesWriteString() throws Exception { when(impl.getKey()).thenReturn("pathOfFilesWriteString"); when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); when(impl.getBody()).thenReturn(IOUtils.toString( - getClass().getResourceAsStream("/PathOfFilesWriteStringMacroActions.groovy"))); + Objects.requireNonNull(getClass().getResourceAsStream( + "/PathOfFilesWriteStringMacroActions.groovy")))); final MacroActions actions = ImplementationManager.build(impl); From 1156e9656f491ec2402994cf6263865f656b9845 Mon Sep 17 00:00:00 2001 From: Massimiliano Perrone Date: Thu, 28 May 2026 08:16:06 +0200 Subject: [PATCH 5/7] Add Groovy sandbox exploit PoC tests --- .../POCBlacklistSandboxTest.java | 474 ++++++++++++++++++ 1 file changed, 474 insertions(+) create mode 100644 core/spring/src/test/java/org/apache/syncope/core/spring/implementation/POCBlacklistSandboxTest.java diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/POCBlacklistSandboxTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/POCBlacklistSandboxTest.java new file mode 100644 index 00000000000..d98b20a7b5f --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/POCBlacklistSandboxTest.java @@ -0,0 +1,474 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.spring.implementation; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.Reader; +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.cxf.helpers.IOUtils; +import org.apache.syncope.common.lib.types.ImplementationEngine; +import org.apache.syncope.core.persistence.api.ApplicationContextProvider; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.provisioning.api.ImplementationLookup; +import org.apache.syncope.core.provisioning.api.macro.MacroActions; +import org.apache.syncope.core.spring.SpringTestConfiguration; +import org.apache.syncope.core.spring.security.DefaultEncryptorManager; +import org.apache.syncope.core.spring.security.DummyImplementationLookup; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.jenkinsci.plugins.scriptsecurity.sandbox.blacklists.Blacklist; +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.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.context.annotation.Primary; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +@SpringJUnitConfig(classes = { POCBlacklistSandboxTest.TmpConfiguration.class }) +class POCBlacklistSandboxTest { + + @TempDir + private Path tempDir; + + @EnableAspectJAutoProxy(proxyTargetClass = false) + @Configuration(proxyBeanMethods = false) + static class TmpConfiguration { + + @Bean + ApplicationContextProvider applicationContextProvider() { + return new ApplicationContextProvider(); + } + + @Bean + EncryptorManager encryptorManager() { + SecurityProperties securityProperties = new SecurityProperties(); + securityProperties.setAesSecretKey(SpringTestConfiguration.AES_SECRET_KEY); + return new DefaultEncryptorManager(securityProperties); + } + + @Primary + @Bean + ImplementationLookup implementationLookup() { + return new DummyImplementationLookup(); + } + + @Bean + Blacklist groovyBlackList() throws IOException { + Path blacklist = Files.createTempFile("tmp-groovy-", ".blacklist"); + blacklist.toFile().deleteOnExit(); + try (Reader reader = Files.newBufferedReader(blacklist)) { + return new Blacklist(reader); + } + } + } + + private MacroActions actions(final String key, final String resource) throws Exception { + Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("tmp-" + key); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + Objects.requireNonNull(getClass().getResourceAsStream(resource)))); + + return ImplementationManager.build(impl); + } + + // Add to tmp-groovy.blacklist: + // staticMethod java.nio.file.Path of java.lang.String java.lang.String[] + @Test + void pocPathOfStringReadsFile() throws Exception { + Path testFile = tempDir.resolve("tmp-path-of-string.txt"); + Files.writeString(testFile, "tmp-path-of-string-ok"); + + StringBuilder output = actions("pathOfString", "/PathOfFilesReadStringMacroActions.groovy"). + afterAll(null, new StringBuilder(testFile.toAbsolutePath().toString())); + + assertTrue(output.toString().contains("tmp-path-of-string-ok")); + } + + // Add to tmp-groovy.blacklist: + // staticMethod java.nio.file.Path of java.net.URI + @Test + void pocPathOfUriReadsFile() throws Exception { + Path testFile = tempDir.resolve("tmp-path-of-uri.txt"); + Files.writeString(testFile, "tmp-path-of-uri-ok"); + + StringBuilder output = actions("pathOfUri", "/PathOfUriFilesReadStringMacroActions.groovy"). + afterAll(null, new StringBuilder(testFile.toUri().toString())); + + assertTrue(output.toString().contains("tmp-path-of-uri-ok")); + } + + // Add to tmp-groovy.blacklist: + // staticMethod java.nio.file.Paths get java.lang.String java.lang.String[] + @Test + void pocPathsGetStringReadsFile() throws Exception { + Path testFile = tempDir.resolve("tmp-paths-get-string.txt"); + Files.writeString(testFile, "tmp-paths-get-string-ok"); + + StringBuilder output = actions("pathsGetString", "/PathsGetFilesReadStringMacroActions.groovy"). + afterAll(null, new StringBuilder(testFile.toAbsolutePath().toString())); + + assertTrue(output.toString().contains("tmp-paths-get-string-ok")); + } + + // Add to tmp-groovy.blacklist: + // staticMethod java.nio.file.Paths get java.net.URI + @Test + void pocPathsGetUriReadsFile() throws Exception { + Path testFile = tempDir.resolve("tmp-paths-get-uri.txt"); + Files.writeString(testFile, "tmp-paths-get-uri-ok"); + + StringBuilder output = actions("pathsGetUri", "/PathsGetUriFilesReadStringMacroActions.groovy"). + afterAll(null, new StringBuilder(testFile.toUri().toString())); + + assertTrue(output.toString().contains("tmp-paths-get-uri-ok")); + } + + // Add to tmp-groovy.blacklist: + // staticMethod java.nio.file.FileSystems getDefault + // method java.nio.file.FileSystem getPath java.lang.String java.lang.String[] + @Test + void pocFileSystemsReadsFile() throws Exception { + Path testFile = tempDir.resolve("tmp-filesystems.txt"); + Files.writeString(testFile, "tmp-filesystems-ok"); + + StringBuilder output = actions("fileSystems", "/FileSystemsReadStringMacroActions.groovy"). + afterAll(null, new StringBuilder(testFile.toAbsolutePath().toString())); + + assertTrue(output.toString().contains("tmp-filesystems-ok")); + } + + // Add to tmp-groovy.blacklist: + // staticMethod java.nio.file.spi.FileSystemProvider installedProviders + // method java.nio.file.spi.FileSystemProvider getPath java.net.URI + @Test + void pocFileSystemProviderReadsFile() throws Exception { + Path testFile = tempDir.resolve("tmp-filesystem-provider.txt"); + Files.writeString(testFile, "tmp-filesystem-provider-ok"); + + StringBuilder output = actions("fileSystemProvider", "/FileSystemProviderReadStringMacroActions.groovy"). + afterAll(null, new StringBuilder(testFile.toUri().toString())); + + assertTrue(output.toString().contains("tmp-filesystem-provider-ok")); + } + + // Add to tmp-groovy.blacklist: + // staticMethod java.nio.file.Files createTempFile java.lang.String java.lang.String java.nio.file.attribute.FileAttribute[] + @Test + void pocFilesCreateTempFileWritesFile() throws Exception { + StringBuilder output = actions("filesCreateTempFile", "/FilesCreateTempFileMacroActions.groovy"). + afterAll(null, new StringBuilder()); + + assertTrue(output.toString().contains("sandbox-files-create-temp-ok")); + } + + // Add to tmp-groovy.blacklist: + // staticMethod java.nio.file.Files createTempDirectory java.lang.String java.nio.file.attribute.FileAttribute[] + @Test + void pocFilesCreateTempDirectoryCreatesDirectory() throws Exception { + StringBuilder output = actions("filesCreateTempDirectory", "/FilesCreateTempDirectoryMacroActions.groovy"). + afterAll(null, new StringBuilder()); + + assertTrue(Files.isDirectory(Path.of(output.toString()))); + } + + // Add to tmp-groovy.blacklist: + // staticMethod java.lang.ProcessBuilder startPipeline java.util.List + @EnabledOnOs(OS.LINUX) + @Test + void pocProcessBuilderStartPipelineExecutesCommand() throws Exception { + @SuppressWarnings("unchecked") + Map ctx = + (Map) (Map) Map.of( + "builders", + List.of(new ProcessBuilder("/bin/sh", "-c", "printf tmp-start-pipeline-ok"))); + + StringBuilder output = actions("processBuilderStartPipeline", "/ProcessBuilderStartPipelineMacroActions.groovy"). + afterAll(ctx, new StringBuilder()); + + assertEquals("tmp-start-pipeline-ok", output.toString()); + } + + // Add to tmp-groovy.blacklist: + // method java.lang.Runtime exec java.lang.String[] + @EnabledOnOs(OS.LINUX) + @Test + void pocRuntimeExecExecutesCommand() throws Exception { + @SuppressWarnings("unchecked") + Map ctx = + (Map) (Map) Map.of("runtime", Runtime.getRuntime()); + + StringBuilder output = actions("runtimeExec", "/RuntimeExecMacroActions.groovy"). + afterAll(ctx, new StringBuilder()); + + assertEquals("sandbox-runtime-exec-ok", output.toString()); + } + + // Add to tmp-groovy.blacklist: + // new java.beans.Expression java.lang.Object java.lang.String java.lang.Object[] + // method java.beans.Expression getValue + @EnabledOnOs(OS.LINUX) + @Test + void pocBeansExpressionExecutesCommand() throws Exception { + StringBuilder output = actions("beansExpression", "/BeansExpressionRuntimeExecMacroActions.groovy"). + afterAll(null, new StringBuilder()); + + assertTrue(output.toString().contains("sandbox-beans-expression-ok")); + } + + // Add to tmp-groovy.blacklist: + // new java.beans.Statement java.lang.Object java.lang.String java.lang.Object[] + @Test + void pocBeansStatementCanBuildSystemExitStatement() throws Exception { + StringBuilder output = actions("beansStatement", "/BeansStatementSystemExitMacroActions.groovy"). + afterAll(null, new StringBuilder()); + + assertTrue(output.toString().contains("exit")); + } + + // Add to tmp-groovy.blacklist: + // new groovy.lang.GroovyShell + // method groovy.lang.GroovyShell evaluate java.lang.String + @EnabledOnOs(OS.LINUX) + @Test + void pocGroovyShellExecutesNestedCode() throws Exception { + StringBuilder output = actions("groovyShell", "/GroovyShellRuntimeExecMacroActions.groovy"). + afterAll(null, new StringBuilder()); + + assertEquals("sandbox-groovy-shell-ok", output.toString()); + } + + // Add to tmp-groovy.blacklist: + // staticMethod groovy.util.Eval me java.lang.String + @EnabledOnOs(OS.LINUX) + @Test + void pocEvalExecutesNestedCode() throws Exception { + StringBuilder output = actions("eval", "/EvalRuntimeExecMacroActions.groovy"). + afterAll(null, new StringBuilder()); + + assertEquals("sandbox-eval-ok", output.toString()); + } + + // Add to tmp-groovy.blacklist: + // staticMethod java.lang.invoke.MethodHandles publicLookup + // method java.lang.invoke.MethodHandles$Lookup findStatic java.lang.Class java.lang.String java.lang.invoke.MethodType + // method java.lang.invoke.MethodHandle invokeWithArguments java.lang.Object[] + @EnabledOnOs(OS.LINUX) + @Test + void pocMethodHandlesExecutesCommand() throws Exception { + StringBuilder output = actions("methodHandles", "/MethodHandlesRuntimeExecMacroActions.groovy"). + afterAll(null, new StringBuilder()); + + assertEquals("sandbox-method-handles-ok", output.toString()); + } + + // Add to tmp-groovy.blacklist, one by one: + // 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 + @EnabledOnOs(OS.LINUX) + @Test + void pocRuntimeExecOverloadsExecuteCommands() throws Exception { + StringBuilder output = actions("runtimeExecOverloads", "/RuntimeExecOverloadsMacroActions.groovy"). + afterAll(null, new StringBuilder()); + + assertEquals( + "runtime-string|runtime-string-env|runtime-string-env-dir|" + + "runtime-array|runtime-array-env|runtime-array-env-dir|", + output.toString()); + } + + // Add to tmp-groovy.blacklist, one by one: + // 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 + @Test + void pocEvalOverloadsExecuteNestedCode() throws Exception { + StringBuilder output = actions("evalOverloads", "/EvalOverloadsMacroActions.groovy"). + afterAll(null, new StringBuilder()); + + assertEquals("eval-me0|eval-me1|eval-x|eval-xy|eval-xyz|", output.toString()); + } + + // Add to tmp-groovy.blacklist, one by one: + // new groovy.lang.GroovyShell + // 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.io.Reader + // method groovy.lang.GroovyShell evaluate java.io.Reader java.lang.String + // method groovy.lang.GroovyShell evaluate java.io.File + // method groovy.lang.GroovyShell evaluate java.net.URI + // method groovy.lang.GroovyShell parse java.lang.String + // method groovy.lang.GroovyShell parse java.io.Reader + // method groovy.lang.GroovyShell run java.lang.String java.lang.String java.util.List + // method groovy.lang.GroovyShell run java.io.Reader java.lang.String java.util.List + @Test + void pocGroovyShellOverloadsEvaluateParseAndRunNestedCode() throws Exception { + Path script = tempDir.resolve("tmp-groovy-shell-overload.groovy"); + Files.writeString(script, "return 'shell-evaluate-file|'"); + @SuppressWarnings("unchecked") + Map ctx = + (Map) (Map) Map.of("scriptFile", script.toFile()); + + StringBuilder output = actions("groovyShellOverloads", "/GroovyShellOverloadsMacroActions.groovy"). + afterAll(ctx, new StringBuilder()); + + assertEquals( + "shell-evaluate-string|shell-evaluate-string-name|shell-evaluate-string-name-codebase|" + + "shell-evaluate-reader|shell-evaluate-reader-name|shell-evaluate-file|shell-evaluate-file|" + + "shell-parse-string|shell-parse-reader|shell-run-string-list|shell-run-reader-list|", + output.toString()); + } + + // Add to tmp-groovy.blacklist, one by one: + // new groovy.lang.GroovyClassLoader + // method groovy.lang.GroovyClassLoader parseClass java.lang.String + // method groovy.lang.GroovyClassLoader parseClass java.lang.String java.lang.String + // method groovy.lang.GroovyClassLoader parseClass java.io.Reader java.lang.String + @Test + void pocGroovyClassLoaderOverloadsCompileNestedCode() throws Exception { + StringBuilder output = actions("groovyClassLoaderOverloads", "/GroovyClassLoaderOverloadsMacroActions.groovy"). + afterAll(null, new StringBuilder()); + + assertEquals("gcl-string|gcl-string-name|gcl-reader-name|", output.toString()); + } + + // Add to tmp-groovy.blacklist, one by one: + // new javax.script.ScriptEngineManager + // method javax.script.ScriptEngineManager getEngineByName java.lang.String + // method javax.script.ScriptEngineManager getEngineByExtension java.lang.String + // method javax.script.ScriptEngine eval java.lang.String + // method javax.script.ScriptEngine eval java.io.Reader + @Test + void pocScriptEngineOverloadsEvaluateNestedCode() throws Exception { + StringBuilder output = actions("scriptEngineOverloads", "/ScriptEngineOverloadsMacroActions.groovy"). + afterAll(null, new StringBuilder()); + + assertEquals("script-engine-string|script-engine-reader|script-engine-extension|", output.toString()); + } + + // Add to tmp-groovy.blacklist, one by one: + // 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 + @Test + void pocFileSystemsOverloadsReachFilesystemApis() throws Exception { + Path file = tempDir.resolve("tmp-filesystems-overloads.txt"); + Files.writeString(file, "tmp-filesystems-overloads-ok"); + @SuppressWarnings("unchecked") + Map ctx = + (Map) (Map) Map.of("path", file); + + StringBuilder output = actions("fileSystemsOverloads", "/FileSystemsOverloadsMacroActions.groovy"). + afterAll(ctx, new StringBuilder()); + + assertEquals( + "fs-get-filesystem|fs-new-uri-map|fs-new-uri-map-loader|" + + "fs-new-path|fs-new-path-loader|fs-new-path-map|fs-new-path-map-loader|", + output.toString()); + } + + // Add to tmp-groovy.blacklist, one by one: + // method java.nio.file.spi.FileSystemProvider getFileSystem 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 + @Test + void pocFileSystemProviderOverloadsReachProviderApis() throws Exception { + Path file = tempDir.resolve("tmp-filesystem-provider-overloads.txt"); + Files.writeString(file, "tmp-filesystem-provider-overloads-ok"); + @SuppressWarnings("unchecked") + Map ctx = + (Map) (Map) Map.of("path", file); + + StringBuilder output = actions("fileSystemProviderOverloads", "/FileSystemProviderOverloadsMacroActions.groovy"). + afterAll(ctx, new StringBuilder()); + + assertEquals("fsp-get-filesystem|fsp-new-uri-map|fsp-new-path-map|", output.toString()); + } + + // Add to tmp-groovy.blacklist, one by one: + // staticMethod 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 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 findConstructor java.lang.Class java.lang.invoke.MethodType + // method java.lang.invoke.MethodHandles$Lookup findStatic java.lang.Class java.lang.String java.lang.invoke.MethodType + // method java.lang.invoke.MethodHandles$Lookup findVirtual java.lang.Class java.lang.String java.lang.invoke.MethodType + @Test + void pocMethodHandlesOverloadsInvokeIndirectly() throws Exception { + StringBuilder output = actions("methodHandlesOverloads", "/MethodHandlesOverloadsMacroActions.groovy"). + afterAll(null, new StringBuilder()); + + assertEquals("7|MH-VIRTUAL|mh-constructor|MH-BIND|toString|", output.toString()); + } + + // Add to tmp-groovy.blacklist, one by one: + // 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 + @Test + void pocBeansOverloadsInvokeIndirectly() throws Exception { + StringBuilder output = actions("beansOverloads", "/BeansOverloadsMacroActions.groovy"). + afterAll(null, new StringBuilder()); + + assertEquals("beans-expression|beans-preset|beans-set-value|beans-statement-execute|", output.toString()); + } + + // Add to tmp-groovy.blacklist: + // staticMethod java.nio.file.Path of java.lang.String java.lang.String[] + @Test + void pocPathOfStringWritesFile() throws Exception { + Path testFile = tempDir.resolve("tmp-path-of-string-write.txt"); + assertFalse(Files.exists(testFile)); + + actions("pathOfStringWrite", "/PathOfFilesWriteStringMacroActions.groovy"). + afterAll(null, new StringBuilder(testFile.toAbsolutePath().toString())); + + assertEquals("sandbox-write-ok", Files.readString(testFile)); + } +} From 1fffa8b28a1508b57e79aa24460c14efa3b744b4 Mon Sep 17 00:00:00 2001 From: Massimiliano Perrone Date: Thu, 28 May 2026 08:16:26 +0200 Subject: [PATCH 6/7] Harden Groovy sandbox blacklist --- .../main/resources/META-INF/groovy.blacklist | 138 +++++++++ .../implementation/GroovySandboxTest.java | 287 +++++++++++++++++- ...nsExpressionRuntimeExecMacroActions.groovy | 35 +++ .../BeansOverloadsMacroActions.groovy | 43 +++ ...eansStatementSystemExitMacroActions.groovy | 31 ++ .../EvalOverloadsMacroActions.groovy | 34 +++ .../EvalRuntimeExecMacroActions.groovy | 31 ++ ...SystemProviderOverloadsMacroActions.groovy | 47 +++ ...ystemProviderReadStringMacroActions.groovy | 32 ++ .../FileSystemsOverloadsMacroActions.groovy | 72 +++++ .../FileSystemsReadStringMacroActions.groovy | 31 ++ ...ilesCreateTempDirectoryMacroActions.groovy | 31 ++ .../FilesCreateTempFileMacroActions.groovy | 32 ++ ...ovyClassLoaderOverloadsMacroActions.groovy | 40 +++ .../GroovyShellOverloadsMacroActions.groovy | 43 +++ .../GroovyShellRuntimeExecMacroActions.groovy | 31 ++ .../MethodHandlesOverloadsMacroActions.groovy | 59 ++++ ...ethodHandlesRuntimeExecMacroActions.groovy | 43 +++ ...athOfUriFilesReadStringMacroActions.groovy | 31 ++ ...PathsGetFilesReadStringMacroActions.groovy | 31 ++ ...hsGetUriFilesReadStringMacroActions.groovy | 31 ++ ...essBuilderStartPipelineMacroActions.groovy | 31 ++ .../resources/RuntimeExecMacroActions.groovy | 31 ++ .../RuntimeExecOverloadsMacroActions.groovy | 42 +++ .../ScriptEngineOverloadsMacroActions.groovy | 36 +++ 25 files changed, 1292 insertions(+), 1 deletion(-) create mode 100644 core/spring/src/test/resources/BeansExpressionRuntimeExecMacroActions.groovy create mode 100644 core/spring/src/test/resources/BeansOverloadsMacroActions.groovy create mode 100644 core/spring/src/test/resources/BeansStatementSystemExitMacroActions.groovy create mode 100644 core/spring/src/test/resources/EvalOverloadsMacroActions.groovy create mode 100644 core/spring/src/test/resources/EvalRuntimeExecMacroActions.groovy create mode 100644 core/spring/src/test/resources/FileSystemProviderOverloadsMacroActions.groovy create mode 100644 core/spring/src/test/resources/FileSystemProviderReadStringMacroActions.groovy create mode 100644 core/spring/src/test/resources/FileSystemsOverloadsMacroActions.groovy create mode 100644 core/spring/src/test/resources/FileSystemsReadStringMacroActions.groovy create mode 100644 core/spring/src/test/resources/FilesCreateTempDirectoryMacroActions.groovy create mode 100644 core/spring/src/test/resources/FilesCreateTempFileMacroActions.groovy create mode 100644 core/spring/src/test/resources/GroovyClassLoaderOverloadsMacroActions.groovy create mode 100644 core/spring/src/test/resources/GroovyShellOverloadsMacroActions.groovy create mode 100644 core/spring/src/test/resources/GroovyShellRuntimeExecMacroActions.groovy create mode 100644 core/spring/src/test/resources/MethodHandlesOverloadsMacroActions.groovy create mode 100644 core/spring/src/test/resources/MethodHandlesRuntimeExecMacroActions.groovy create mode 100644 core/spring/src/test/resources/PathOfUriFilesReadStringMacroActions.groovy create mode 100644 core/spring/src/test/resources/PathsGetFilesReadStringMacroActions.groovy create mode 100644 core/spring/src/test/resources/PathsGetUriFilesReadStringMacroActions.groovy create mode 100644 core/spring/src/test/resources/ProcessBuilderStartPipelineMacroActions.groovy create mode 100644 core/spring/src/test/resources/RuntimeExecMacroActions.groovy create mode 100644 core/spring/src/test/resources/RuntimeExecOverloadsMacroActions.groovy create mode 100644 core/spring/src/test/resources/ScriptEngineOverloadsMacroActions.groovy diff --git a/core/spring/src/main/resources/META-INF/groovy.blacklist b/core/spring/src/main/resources/META-INF/groovy.blacklist index d4f7f5b7bba..cfe5dfeccd8 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. @@ -85,6 +169,60 @@ 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 09c74d0f3f1..26410afdd30 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 @@ -25,8 +25,9 @@ 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; @@ -130,4 +131,288 @@ void pathOfFilesWriteString() throws Exception { 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 Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("pathOfUriFilesReadString"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + Objects.requireNonNull(getClass().getResourceAsStream( + "/PathOfUriFilesReadStringMacroActions.groovy")))); + + final MacroActions actions = ImplementationManager.build(impl); + + 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 Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("pathsGetFilesReadString"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + Objects.requireNonNull(getClass().getResourceAsStream( + "/PathsGetFilesReadStringMacroActions.groovy")))); + + final MacroActions actions = ImplementationManager.build(impl); + + 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 Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("pathsGetUriFilesReadString"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + Objects.requireNonNull(getClass().getResourceAsStream( + "/PathsGetUriFilesReadStringMacroActions.groovy")))); + + final MacroActions actions = ImplementationManager.build(impl); + + 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"); + + Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("fileSystemsReadString"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + Objects.requireNonNull(getClass().getResourceAsStream( + "/FileSystemsReadStringMacroActions.groovy")))); + + final MacroActions actions = ImplementationManager.build(impl); + + 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"); + + Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("fileSystemProviderReadString"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + Objects.requireNonNull(getClass().getResourceAsStream( + "/FileSystemProviderReadStringMacroActions.groovy")))); + + final MacroActions actions = ImplementationManager.build(impl); + + 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 { + Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("filesCreateTempFile"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + Objects.requireNonNull(getClass().getResourceAsStream( + "/FilesCreateTempFileMacroActions.groovy")))); + + final MacroActions actions = ImplementationManager.build(impl); + + 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 { + Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("filesCreateTempDirectory"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + Objects.requireNonNull(getClass().getResourceAsStream( + "/FilesCreateTempDirectoryMacroActions.groovy")))); + + final MacroActions actions = ImplementationManager.build(impl); + + 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 { + Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("processBuilderStartPipeline"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + Objects.requireNonNull(getClass().getResourceAsStream( + "/ProcessBuilderStartPipelineMacroActions.groovy")))); + + final MacroActions actions = ImplementationManager.build(impl); + + @SuppressWarnings("unchecked") + final Map ctx = + (Map) (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 { + Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("runtimeExec"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + Objects.requireNonNull(getClass().getResourceAsStream( + "/RuntimeExecMacroActions.groovy")))); + + final MacroActions actions = ImplementationManager.build(impl); + + @SuppressWarnings("unchecked") + final Map ctx = + (Map) (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 { + Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("beansExpressionRuntimeExec"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + Objects.requireNonNull(getClass().getResourceAsStream( + "/BeansExpressionRuntimeExecMacroActions.groovy")))); + + final MacroActions actions = ImplementationManager.build(impl); + + 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 { + Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("beansStatementSystemExit"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + Objects.requireNonNull(getClass().getResourceAsStream( + "/BeansStatementSystemExitMacroActions.groovy")))); + + final MacroActions actions = ImplementationManager.build(impl); + + 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 { + Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("groovyShellRuntimeExec"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + Objects.requireNonNull(getClass().getResourceAsStream( + "/GroovyShellRuntimeExecMacroActions.groovy")))); + + final MacroActions actions = ImplementationManager.build(impl); + + 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 { + Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("evalRuntimeExec"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + Objects.requireNonNull(getClass().getResourceAsStream( + "/EvalRuntimeExecMacroActions.groovy")))); + + final MacroActions actions = ImplementationManager.build(impl); + + 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 { + Implementation impl = mock(Implementation.class); + when(impl.getKey()).thenReturn("methodHandlesRuntimeExec"); + when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); + when(impl.getBody()).thenReturn(IOUtils.toString( + Objects.requireNonNull(getClass().getResourceAsStream( + "/MethodHandlesRuntimeExecMacroActions.groovy")))); + + final MacroActions actions = ImplementationManager.build(impl); + + final SecurityException e = assertThrows( + SecurityException.class, + () -> actions.afterAll(null, new StringBuilder())); + assertTrue(e.getMessage().contains( + "Insecure call to 'staticMethod java.lang.invoke.MethodHandles publicLookup'")); + } } diff --git a/core/spring/src/test/resources/BeansExpressionRuntimeExecMacroActions.groovy b/core/spring/src/test/resources/BeansExpressionRuntimeExecMacroActions.groovy new file mode 100644 index 00000000000..494f0925fc6 --- /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 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 00000000000..7c7543a1b86 --- /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 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 00000000000..1d5256d8ecb --- /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 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 00000000000..473925e2713 --- /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 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 00000000000..fd62046bbbf --- /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 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 00000000000..8d4a7d4bcb0 --- /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 ctx, StringBuilder output) { + def provider = java.nio.file.spi.FileSystemProvider.installedProviders().find { it.scheme == 'file' } + 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 00000000000..0e9cb09af42 --- /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 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 00000000000..788f4f45d0b --- /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 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 00000000000..e7bf14ecbbc --- /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 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 00000000000..d95263b2300 --- /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 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 00000000000..c82355236f8 --- /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 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 00000000000..f797a37d0ba --- /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 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 00000000000..eed450af195 --- /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 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 00000000000..0bf2aef7fbf --- /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 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 00000000000..956d8c75833 --- /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 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 00000000000..fffbb18f62f --- /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 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/PathOfUriFilesReadStringMacroActions.groovy b/core/spring/src/test/resources/PathOfUriFilesReadStringMacroActions.groovy new file mode 100644 index 00000000000..a45adc1ec7f --- /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 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 00000000000..e0db1fb3da9 --- /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 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 00000000000..a27bab09dc3 --- /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 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 00000000000..14be68b400c --- /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 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 00000000000..b9e5d1eb515 --- /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 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 00000000000..e9145e9dc82 --- /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 ctx, StringBuilder output) { + def runtime = java.lang.Runtime.getRuntime() + 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 00000000000..2bde95c36d4 --- /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 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 +} From 2b35884acc9169a26c31b07b97fbe7bbdf20c96e Mon Sep 17 00:00:00 2001 From: Massimiliano Perrone Date: Fri, 29 May 2026 09:06:13 +0200 Subject: [PATCH 7/7] Harden Groovy sandbox against filesystem and execution bypasses --- .../implementation/GroovySandboxTest.java | 318 ++++++------ .../POCBlacklistSandboxTest.java | 474 ------------------ ...SystemProviderOverloadsMacroActions.groovy | 2 +- .../RuntimeExecOverloadsMacroActions.groovy | 2 +- 4 files changed, 168 insertions(+), 628 deletions(-) delete mode 100644 core/spring/src/test/java/org/apache/syncope/core/spring/implementation/POCBlacklistSandboxTest.java 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 26410afdd30..fe2e0641bfe 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 @@ -49,45 +49,40 @@ class GroovySandboxTest { @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( - Objects.requireNonNull(getClass().getResourceAsStream("/ProcessBuilderMacroActions.groovy")))); - - MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = actions("processBuilder", "/ProcessBuilderMacroActions.groovy"); - 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( - Objects.requireNonNull(getClass().getResourceAsStream("/BashMacroActions.groovy")))); + Objects.requireNonNull(getClass().getResourceAsStream(resource)))); + + return ImplementationManager.build(impl); + } - MacroActions actions = ImplementationManager.build(impl); + @EnabledOnOs(OS.LINUX) + @Test + void bash() throws Exception { + final MacroActions actions = actions("bash", "/BashMacroActions.groovy"); - SecurityException e = assertThrows( + 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( - Objects.requireNonNull(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'")); } @@ -96,14 +91,7 @@ void pathOfFilesReadString() throws Exception { final Path testFile = tempDir.resolve("sandbox-read.txt"); Files.writeString(testFile, "sandbox-read-ok"); - final Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("pathOfFilesReadString"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/PathOfFilesReadStringMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = actions("pathOfFilesReadString", "/PathOfFilesReadStringMacroActions.groovy"); final SecurityException e = assertThrows( SecurityException.class, @@ -116,14 +104,7 @@ void pathOfFilesReadString() throws Exception { void pathOfFilesWriteString() throws Exception { final Path testFile = tempDir.resolve("sandbox-write.txt"); - Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("pathOfFilesWriteString"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/PathOfFilesWriteStringMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = actions("pathOfFilesWriteString", "/PathOfFilesWriteStringMacroActions.groovy"); final SecurityException e = assertThrows( SecurityException.class, @@ -137,14 +118,8 @@ void pathOfUriFilesReadString() throws Exception { final Path testFile = tempDir.resolve("sandbox-read-uri.txt"); Files.writeString(testFile, "sandbox-read-uri-ok"); - final Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("pathOfUriFilesReadString"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/PathOfUriFilesReadStringMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = + actions("pathOfUriFilesReadString", "/PathOfUriFilesReadStringMacroActions.groovy"); final SecurityException e = assertThrows( SecurityException.class, @@ -158,14 +133,7 @@ void pathsGetFilesReadString() throws Exception { final Path testFile = tempDir.resolve("sandbox-paths-get-read.txt"); Files.writeString(testFile, "sandbox-paths-get-read-ok"); - final Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("pathsGetFilesReadString"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/PathsGetFilesReadStringMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = actions("pathsGetFilesReadString", "/PathsGetFilesReadStringMacroActions.groovy"); final SecurityException e = assertThrows( SecurityException.class, @@ -179,14 +147,8 @@ 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 Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("pathsGetUriFilesReadString"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/PathsGetUriFilesReadStringMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = + actions("pathsGetUriFilesReadString", "/PathsGetUriFilesReadStringMacroActions.groovy"); final SecurityException e = assertThrows( SecurityException.class, @@ -200,14 +162,7 @@ void fileSystemsReadString() throws Exception { final Path testFile = tempDir.resolve("sandbox-filesystems-read.txt"); Files.writeString(testFile, "sandbox-filesystems-read-ok"); - Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("fileSystemsReadString"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/FileSystemsReadStringMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = actions("fileSystemsReadString", "/FileSystemsReadStringMacroActions.groovy"); final SecurityException e = assertThrows( SecurityException.class, @@ -221,14 +176,8 @@ void fileSystemProviderReadString() throws Exception { final Path testFile = tempDir.resolve("sandbox-filesystem-provider-read.txt"); Files.writeString(testFile, "sandbox-filesystem-provider-read-ok"); - Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("fileSystemProviderReadString"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/FileSystemProviderReadStringMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = + actions("fileSystemProviderReadString", "/FileSystemProviderReadStringMacroActions.groovy"); final SecurityException e = assertThrows( SecurityException.class, @@ -239,14 +188,7 @@ void fileSystemProviderReadString() throws Exception { @Test void filesCreateTempFile() throws Exception { - Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("filesCreateTempFile"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/FilesCreateTempFileMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = actions("filesCreateTempFile", "/FilesCreateTempFileMacroActions.groovy"); final SecurityException e = assertThrows( SecurityException.class, @@ -258,14 +200,8 @@ void filesCreateTempFile() throws Exception { @Test void filesCreateTempDirectory() throws Exception { - Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("filesCreateTempDirectory"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/FilesCreateTempDirectoryMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = + actions("filesCreateTempDirectory", "/FilesCreateTempDirectoryMacroActions.groovy"); final SecurityException e = assertThrows( SecurityException.class, @@ -278,14 +214,8 @@ void filesCreateTempDirectory() throws Exception { @EnabledOnOs(OS.LINUX) @Test void processBuilderStartPipeline() throws Exception { - Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("processBuilderStartPipeline"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/ProcessBuilderStartPipelineMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = + actions("processBuilderStartPipeline", "/ProcessBuilderStartPipelineMacroActions.groovy"); @SuppressWarnings("unchecked") final Map ctx = @@ -303,14 +233,7 @@ void processBuilderStartPipeline() throws Exception { @EnabledOnOs(OS.LINUX) @Test void runtimeExec() throws Exception { - Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("runtimeExec"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/RuntimeExecMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = actions("runtimeExec", "/RuntimeExecMacroActions.groovy"); @SuppressWarnings("unchecked") final Map ctx = @@ -326,14 +249,8 @@ void runtimeExec() throws Exception { @EnabledOnOs(OS.LINUX) @Test void beansExpressionRuntimeExec() throws Exception { - Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("beansExpressionRuntimeExec"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/BeansExpressionRuntimeExecMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = + actions("beansExpressionRuntimeExec", "/BeansExpressionRuntimeExecMacroActions.groovy"); final SecurityException e = assertThrows( SecurityException.class, @@ -344,14 +261,8 @@ void beansExpressionRuntimeExec() throws Exception { @Test void beansStatementSystemExit() throws Exception { - Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("beansStatementSystemExit"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/BeansStatementSystemExitMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = + actions("beansStatementSystemExit", "/BeansStatementSystemExitMacroActions.groovy"); final SecurityException e = assertThrows( SecurityException.class, @@ -363,14 +274,7 @@ void beansStatementSystemExit() throws Exception { @EnabledOnOs(OS.LINUX) @Test void groovyShellRuntimeExec() throws Exception { - Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("groovyShellRuntimeExec"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/GroovyShellRuntimeExecMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = actions("groovyShellRuntimeExec", "/GroovyShellRuntimeExecMacroActions.groovy"); final SecurityException e = assertThrows( SecurityException.class, @@ -381,14 +285,7 @@ void groovyShellRuntimeExec() throws Exception { @EnabledOnOs(OS.LINUX) @Test void evalRuntimeExec() throws Exception { - Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("evalRuntimeExec"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/EvalRuntimeExecMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = actions("evalRuntimeExec", "/EvalRuntimeExecMacroActions.groovy"); final SecurityException e = assertThrows( SecurityException.class, @@ -400,14 +297,8 @@ void evalRuntimeExec() throws Exception { @EnabledOnOs(OS.LINUX) @Test void methodHandlesRuntimeExec() throws Exception { - Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("methodHandlesRuntimeExec"); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream( - "/MethodHandlesRuntimeExecMacroActions.groovy")))); - - final MacroActions actions = ImplementationManager.build(impl); + final MacroActions actions = + actions("methodHandlesRuntimeExec", "/MethodHandlesRuntimeExecMacroActions.groovy"); final SecurityException e = assertThrows( SecurityException.class, @@ -415,4 +306,127 @@ void methodHandlesRuntimeExec() throws Exception { 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 ctx = + (Map) (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 ctx = + (Map) (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 ctx = + (Map) (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 ctx = + (Map) (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/java/org/apache/syncope/core/spring/implementation/POCBlacklistSandboxTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/POCBlacklistSandboxTest.java deleted file mode 100644 index d98b20a7b5f..00000000000 --- a/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/POCBlacklistSandboxTest.java +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.spring.implementation; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.io.Reader; -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.cxf.helpers.IOUtils; -import org.apache.syncope.common.lib.types.ImplementationEngine; -import org.apache.syncope.core.persistence.api.ApplicationContextProvider; -import org.apache.syncope.core.persistence.api.EncryptorManager; -import org.apache.syncope.core.persistence.api.entity.Implementation; -import org.apache.syncope.core.provisioning.api.ImplementationLookup; -import org.apache.syncope.core.provisioning.api.macro.MacroActions; -import org.apache.syncope.core.spring.SpringTestConfiguration; -import org.apache.syncope.core.spring.security.DefaultEncryptorManager; -import org.apache.syncope.core.spring.security.DummyImplementationLookup; -import org.apache.syncope.core.spring.security.SecurityProperties; -import org.jenkinsci.plugins.scriptsecurity.sandbox.blacklists.Blacklist; -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.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.EnableAspectJAutoProxy; -import org.springframework.context.annotation.Primary; -import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; - -@SpringJUnitConfig(classes = { POCBlacklistSandboxTest.TmpConfiguration.class }) -class POCBlacklistSandboxTest { - - @TempDir - private Path tempDir; - - @EnableAspectJAutoProxy(proxyTargetClass = false) - @Configuration(proxyBeanMethods = false) - static class TmpConfiguration { - - @Bean - ApplicationContextProvider applicationContextProvider() { - return new ApplicationContextProvider(); - } - - @Bean - EncryptorManager encryptorManager() { - SecurityProperties securityProperties = new SecurityProperties(); - securityProperties.setAesSecretKey(SpringTestConfiguration.AES_SECRET_KEY); - return new DefaultEncryptorManager(securityProperties); - } - - @Primary - @Bean - ImplementationLookup implementationLookup() { - return new DummyImplementationLookup(); - } - - @Bean - Blacklist groovyBlackList() throws IOException { - Path blacklist = Files.createTempFile("tmp-groovy-", ".blacklist"); - blacklist.toFile().deleteOnExit(); - try (Reader reader = Files.newBufferedReader(blacklist)) { - return new Blacklist(reader); - } - } - } - - private MacroActions actions(final String key, final String resource) throws Exception { - Implementation impl = mock(Implementation.class); - when(impl.getKey()).thenReturn("tmp-" + key); - when(impl.getEngine()).thenReturn(ImplementationEngine.GROOVY); - when(impl.getBody()).thenReturn(IOUtils.toString( - Objects.requireNonNull(getClass().getResourceAsStream(resource)))); - - return ImplementationManager.build(impl); - } - - // Add to tmp-groovy.blacklist: - // staticMethod java.nio.file.Path of java.lang.String java.lang.String[] - @Test - void pocPathOfStringReadsFile() throws Exception { - Path testFile = tempDir.resolve("tmp-path-of-string.txt"); - Files.writeString(testFile, "tmp-path-of-string-ok"); - - StringBuilder output = actions("pathOfString", "/PathOfFilesReadStringMacroActions.groovy"). - afterAll(null, new StringBuilder(testFile.toAbsolutePath().toString())); - - assertTrue(output.toString().contains("tmp-path-of-string-ok")); - } - - // Add to tmp-groovy.blacklist: - // staticMethod java.nio.file.Path of java.net.URI - @Test - void pocPathOfUriReadsFile() throws Exception { - Path testFile = tempDir.resolve("tmp-path-of-uri.txt"); - Files.writeString(testFile, "tmp-path-of-uri-ok"); - - StringBuilder output = actions("pathOfUri", "/PathOfUriFilesReadStringMacroActions.groovy"). - afterAll(null, new StringBuilder(testFile.toUri().toString())); - - assertTrue(output.toString().contains("tmp-path-of-uri-ok")); - } - - // Add to tmp-groovy.blacklist: - // staticMethod java.nio.file.Paths get java.lang.String java.lang.String[] - @Test - void pocPathsGetStringReadsFile() throws Exception { - Path testFile = tempDir.resolve("tmp-paths-get-string.txt"); - Files.writeString(testFile, "tmp-paths-get-string-ok"); - - StringBuilder output = actions("pathsGetString", "/PathsGetFilesReadStringMacroActions.groovy"). - afterAll(null, new StringBuilder(testFile.toAbsolutePath().toString())); - - assertTrue(output.toString().contains("tmp-paths-get-string-ok")); - } - - // Add to tmp-groovy.blacklist: - // staticMethod java.nio.file.Paths get java.net.URI - @Test - void pocPathsGetUriReadsFile() throws Exception { - Path testFile = tempDir.resolve("tmp-paths-get-uri.txt"); - Files.writeString(testFile, "tmp-paths-get-uri-ok"); - - StringBuilder output = actions("pathsGetUri", "/PathsGetUriFilesReadStringMacroActions.groovy"). - afterAll(null, new StringBuilder(testFile.toUri().toString())); - - assertTrue(output.toString().contains("tmp-paths-get-uri-ok")); - } - - // Add to tmp-groovy.blacklist: - // staticMethod java.nio.file.FileSystems getDefault - // method java.nio.file.FileSystem getPath java.lang.String java.lang.String[] - @Test - void pocFileSystemsReadsFile() throws Exception { - Path testFile = tempDir.resolve("tmp-filesystems.txt"); - Files.writeString(testFile, "tmp-filesystems-ok"); - - StringBuilder output = actions("fileSystems", "/FileSystemsReadStringMacroActions.groovy"). - afterAll(null, new StringBuilder(testFile.toAbsolutePath().toString())); - - assertTrue(output.toString().contains("tmp-filesystems-ok")); - } - - // Add to tmp-groovy.blacklist: - // staticMethod java.nio.file.spi.FileSystemProvider installedProviders - // method java.nio.file.spi.FileSystemProvider getPath java.net.URI - @Test - void pocFileSystemProviderReadsFile() throws Exception { - Path testFile = tempDir.resolve("tmp-filesystem-provider.txt"); - Files.writeString(testFile, "tmp-filesystem-provider-ok"); - - StringBuilder output = actions("fileSystemProvider", "/FileSystemProviderReadStringMacroActions.groovy"). - afterAll(null, new StringBuilder(testFile.toUri().toString())); - - assertTrue(output.toString().contains("tmp-filesystem-provider-ok")); - } - - // Add to tmp-groovy.blacklist: - // staticMethod java.nio.file.Files createTempFile java.lang.String java.lang.String java.nio.file.attribute.FileAttribute[] - @Test - void pocFilesCreateTempFileWritesFile() throws Exception { - StringBuilder output = actions("filesCreateTempFile", "/FilesCreateTempFileMacroActions.groovy"). - afterAll(null, new StringBuilder()); - - assertTrue(output.toString().contains("sandbox-files-create-temp-ok")); - } - - // Add to tmp-groovy.blacklist: - // staticMethod java.nio.file.Files createTempDirectory java.lang.String java.nio.file.attribute.FileAttribute[] - @Test - void pocFilesCreateTempDirectoryCreatesDirectory() throws Exception { - StringBuilder output = actions("filesCreateTempDirectory", "/FilesCreateTempDirectoryMacroActions.groovy"). - afterAll(null, new StringBuilder()); - - assertTrue(Files.isDirectory(Path.of(output.toString()))); - } - - // Add to tmp-groovy.blacklist: - // staticMethod java.lang.ProcessBuilder startPipeline java.util.List - @EnabledOnOs(OS.LINUX) - @Test - void pocProcessBuilderStartPipelineExecutesCommand() throws Exception { - @SuppressWarnings("unchecked") - Map ctx = - (Map) (Map) Map.of( - "builders", - List.of(new ProcessBuilder("/bin/sh", "-c", "printf tmp-start-pipeline-ok"))); - - StringBuilder output = actions("processBuilderStartPipeline", "/ProcessBuilderStartPipelineMacroActions.groovy"). - afterAll(ctx, new StringBuilder()); - - assertEquals("tmp-start-pipeline-ok", output.toString()); - } - - // Add to tmp-groovy.blacklist: - // method java.lang.Runtime exec java.lang.String[] - @EnabledOnOs(OS.LINUX) - @Test - void pocRuntimeExecExecutesCommand() throws Exception { - @SuppressWarnings("unchecked") - Map ctx = - (Map) (Map) Map.of("runtime", Runtime.getRuntime()); - - StringBuilder output = actions("runtimeExec", "/RuntimeExecMacroActions.groovy"). - afterAll(ctx, new StringBuilder()); - - assertEquals("sandbox-runtime-exec-ok", output.toString()); - } - - // Add to tmp-groovy.blacklist: - // new java.beans.Expression java.lang.Object java.lang.String java.lang.Object[] - // method java.beans.Expression getValue - @EnabledOnOs(OS.LINUX) - @Test - void pocBeansExpressionExecutesCommand() throws Exception { - StringBuilder output = actions("beansExpression", "/BeansExpressionRuntimeExecMacroActions.groovy"). - afterAll(null, new StringBuilder()); - - assertTrue(output.toString().contains("sandbox-beans-expression-ok")); - } - - // Add to tmp-groovy.blacklist: - // new java.beans.Statement java.lang.Object java.lang.String java.lang.Object[] - @Test - void pocBeansStatementCanBuildSystemExitStatement() throws Exception { - StringBuilder output = actions("beansStatement", "/BeansStatementSystemExitMacroActions.groovy"). - afterAll(null, new StringBuilder()); - - assertTrue(output.toString().contains("exit")); - } - - // Add to tmp-groovy.blacklist: - // new groovy.lang.GroovyShell - // method groovy.lang.GroovyShell evaluate java.lang.String - @EnabledOnOs(OS.LINUX) - @Test - void pocGroovyShellExecutesNestedCode() throws Exception { - StringBuilder output = actions("groovyShell", "/GroovyShellRuntimeExecMacroActions.groovy"). - afterAll(null, new StringBuilder()); - - assertEquals("sandbox-groovy-shell-ok", output.toString()); - } - - // Add to tmp-groovy.blacklist: - // staticMethod groovy.util.Eval me java.lang.String - @EnabledOnOs(OS.LINUX) - @Test - void pocEvalExecutesNestedCode() throws Exception { - StringBuilder output = actions("eval", "/EvalRuntimeExecMacroActions.groovy"). - afterAll(null, new StringBuilder()); - - assertEquals("sandbox-eval-ok", output.toString()); - } - - // Add to tmp-groovy.blacklist: - // staticMethod java.lang.invoke.MethodHandles publicLookup - // method java.lang.invoke.MethodHandles$Lookup findStatic java.lang.Class java.lang.String java.lang.invoke.MethodType - // method java.lang.invoke.MethodHandle invokeWithArguments java.lang.Object[] - @EnabledOnOs(OS.LINUX) - @Test - void pocMethodHandlesExecutesCommand() throws Exception { - StringBuilder output = actions("methodHandles", "/MethodHandlesRuntimeExecMacroActions.groovy"). - afterAll(null, new StringBuilder()); - - assertEquals("sandbox-method-handles-ok", output.toString()); - } - - // Add to tmp-groovy.blacklist, one by one: - // 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 - @EnabledOnOs(OS.LINUX) - @Test - void pocRuntimeExecOverloadsExecuteCommands() throws Exception { - StringBuilder output = actions("runtimeExecOverloads", "/RuntimeExecOverloadsMacroActions.groovy"). - afterAll(null, new StringBuilder()); - - assertEquals( - "runtime-string|runtime-string-env|runtime-string-env-dir|" - + "runtime-array|runtime-array-env|runtime-array-env-dir|", - output.toString()); - } - - // Add to tmp-groovy.blacklist, one by one: - // 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 - @Test - void pocEvalOverloadsExecuteNestedCode() throws Exception { - StringBuilder output = actions("evalOverloads", "/EvalOverloadsMacroActions.groovy"). - afterAll(null, new StringBuilder()); - - assertEquals("eval-me0|eval-me1|eval-x|eval-xy|eval-xyz|", output.toString()); - } - - // Add to tmp-groovy.blacklist, one by one: - // new groovy.lang.GroovyShell - // 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.io.Reader - // method groovy.lang.GroovyShell evaluate java.io.Reader java.lang.String - // method groovy.lang.GroovyShell evaluate java.io.File - // method groovy.lang.GroovyShell evaluate java.net.URI - // method groovy.lang.GroovyShell parse java.lang.String - // method groovy.lang.GroovyShell parse java.io.Reader - // method groovy.lang.GroovyShell run java.lang.String java.lang.String java.util.List - // method groovy.lang.GroovyShell run java.io.Reader java.lang.String java.util.List - @Test - void pocGroovyShellOverloadsEvaluateParseAndRunNestedCode() throws Exception { - Path script = tempDir.resolve("tmp-groovy-shell-overload.groovy"); - Files.writeString(script, "return 'shell-evaluate-file|'"); - @SuppressWarnings("unchecked") - Map ctx = - (Map) (Map) Map.of("scriptFile", script.toFile()); - - StringBuilder output = actions("groovyShellOverloads", "/GroovyShellOverloadsMacroActions.groovy"). - afterAll(ctx, new StringBuilder()); - - assertEquals( - "shell-evaluate-string|shell-evaluate-string-name|shell-evaluate-string-name-codebase|" - + "shell-evaluate-reader|shell-evaluate-reader-name|shell-evaluate-file|shell-evaluate-file|" - + "shell-parse-string|shell-parse-reader|shell-run-string-list|shell-run-reader-list|", - output.toString()); - } - - // Add to tmp-groovy.blacklist, one by one: - // new groovy.lang.GroovyClassLoader - // method groovy.lang.GroovyClassLoader parseClass java.lang.String - // method groovy.lang.GroovyClassLoader parseClass java.lang.String java.lang.String - // method groovy.lang.GroovyClassLoader parseClass java.io.Reader java.lang.String - @Test - void pocGroovyClassLoaderOverloadsCompileNestedCode() throws Exception { - StringBuilder output = actions("groovyClassLoaderOverloads", "/GroovyClassLoaderOverloadsMacroActions.groovy"). - afterAll(null, new StringBuilder()); - - assertEquals("gcl-string|gcl-string-name|gcl-reader-name|", output.toString()); - } - - // Add to tmp-groovy.blacklist, one by one: - // new javax.script.ScriptEngineManager - // method javax.script.ScriptEngineManager getEngineByName java.lang.String - // method javax.script.ScriptEngineManager getEngineByExtension java.lang.String - // method javax.script.ScriptEngine eval java.lang.String - // method javax.script.ScriptEngine eval java.io.Reader - @Test - void pocScriptEngineOverloadsEvaluateNestedCode() throws Exception { - StringBuilder output = actions("scriptEngineOverloads", "/ScriptEngineOverloadsMacroActions.groovy"). - afterAll(null, new StringBuilder()); - - assertEquals("script-engine-string|script-engine-reader|script-engine-extension|", output.toString()); - } - - // Add to tmp-groovy.blacklist, one by one: - // 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 - @Test - void pocFileSystemsOverloadsReachFilesystemApis() throws Exception { - Path file = tempDir.resolve("tmp-filesystems-overloads.txt"); - Files.writeString(file, "tmp-filesystems-overloads-ok"); - @SuppressWarnings("unchecked") - Map ctx = - (Map) (Map) Map.of("path", file); - - StringBuilder output = actions("fileSystemsOverloads", "/FileSystemsOverloadsMacroActions.groovy"). - afterAll(ctx, new StringBuilder()); - - assertEquals( - "fs-get-filesystem|fs-new-uri-map|fs-new-uri-map-loader|" - + "fs-new-path|fs-new-path-loader|fs-new-path-map|fs-new-path-map-loader|", - output.toString()); - } - - // Add to tmp-groovy.blacklist, one by one: - // method java.nio.file.spi.FileSystemProvider getFileSystem 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 - @Test - void pocFileSystemProviderOverloadsReachProviderApis() throws Exception { - Path file = tempDir.resolve("tmp-filesystem-provider-overloads.txt"); - Files.writeString(file, "tmp-filesystem-provider-overloads-ok"); - @SuppressWarnings("unchecked") - Map ctx = - (Map) (Map) Map.of("path", file); - - StringBuilder output = actions("fileSystemProviderOverloads", "/FileSystemProviderOverloadsMacroActions.groovy"). - afterAll(ctx, new StringBuilder()); - - assertEquals("fsp-get-filesystem|fsp-new-uri-map|fsp-new-path-map|", output.toString()); - } - - // Add to tmp-groovy.blacklist, one by one: - // staticMethod 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 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 findConstructor java.lang.Class java.lang.invoke.MethodType - // method java.lang.invoke.MethodHandles$Lookup findStatic java.lang.Class java.lang.String java.lang.invoke.MethodType - // method java.lang.invoke.MethodHandles$Lookup findVirtual java.lang.Class java.lang.String java.lang.invoke.MethodType - @Test - void pocMethodHandlesOverloadsInvokeIndirectly() throws Exception { - StringBuilder output = actions("methodHandlesOverloads", "/MethodHandlesOverloadsMacroActions.groovy"). - afterAll(null, new StringBuilder()); - - assertEquals("7|MH-VIRTUAL|mh-constructor|MH-BIND|toString|", output.toString()); - } - - // Add to tmp-groovy.blacklist, one by one: - // 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 - @Test - void pocBeansOverloadsInvokeIndirectly() throws Exception { - StringBuilder output = actions("beansOverloads", "/BeansOverloadsMacroActions.groovy"). - afterAll(null, new StringBuilder()); - - assertEquals("beans-expression|beans-preset|beans-set-value|beans-statement-execute|", output.toString()); - } - - // Add to tmp-groovy.blacklist: - // staticMethod java.nio.file.Path of java.lang.String java.lang.String[] - @Test - void pocPathOfStringWritesFile() throws Exception { - Path testFile = tempDir.resolve("tmp-path-of-string-write.txt"); - assertFalse(Files.exists(testFile)); - - actions("pathOfStringWrite", "/PathOfFilesWriteStringMacroActions.groovy"). - afterAll(null, new StringBuilder(testFile.toAbsolutePath().toString())); - - assertEquals("sandbox-write-ok", Files.readString(testFile)); - } -} diff --git a/core/spring/src/test/resources/FileSystemProviderOverloadsMacroActions.groovy b/core/spring/src/test/resources/FileSystemProviderOverloadsMacroActions.groovy index 8d4a7d4bcb0..d224b95508f 100644 --- a/core/spring/src/test/resources/FileSystemProviderOverloadsMacroActions.groovy +++ b/core/spring/src/test/resources/FileSystemProviderOverloadsMacroActions.groovy @@ -25,7 +25,7 @@ abstract class FileSystemProviderOverloadsMacroActions extends Script implements @Override StringBuilder afterAll(Map ctx, StringBuilder output) { - def provider = java.nio.file.spi.FileSystemProvider.installedProviders().find { it.scheme == 'file' } + 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:///')) diff --git a/core/spring/src/test/resources/RuntimeExecOverloadsMacroActions.groovy b/core/spring/src/test/resources/RuntimeExecOverloadsMacroActions.groovy index e9145e9dc82..a57a61a4741 100644 --- a/core/spring/src/test/resources/RuntimeExecOverloadsMacroActions.groovy +++ b/core/spring/src/test/resources/RuntimeExecOverloadsMacroActions.groovy @@ -25,7 +25,7 @@ abstract class RuntimeExecOverloadsMacroActions extends Script implements MacroA @Override StringBuilder afterAll(Map ctx, StringBuilder output) { - def runtime = java.lang.Runtime.getRuntime() + def runtime = ctx.runtime as java.lang.Runtime def append = { Process process -> process.waitFor() output.append(process.inputStream.text)