diff --git a/.github/workflows/backend-build-test.yml b/.github/workflows/backend-build-test.yml index 34917e8e50e..bac52c86cb9 100644 --- a/.github/workflows/backend-build-test.yml +++ b/.github/workflows/backend-build-test.yml @@ -52,6 +52,7 @@ jobs: uses: codecov/codecov-action@v4.0.1 with: token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false verbose: true - name: Build Image diff --git a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/PluginServiceImpl.java b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/PluginServiceImpl.java index 963275738c2..a8206b35bf9 100644 --- a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/PluginServiceImpl.java +++ b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/PluginServiceImpl.java @@ -490,6 +490,10 @@ private List loadLibInPlugin(String pluginJarPath, Long pluginMetadataId) t while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); File file = new File(libDir, entry.getName()); + String canonicalLibDir = libDir.getCanonicalPath() + File.separator; + if (!file.getCanonicalPath().startsWith(canonicalLibDir)) { + throw new IOException("Zip Slip detected: " + entry.getName()); + } if (entry.isDirectory()) { continue; } diff --git a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/PluginServiceTest.java b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/PluginServiceTest.java index fef26765d3e..5c3181c914a 100644 --- a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/PluginServiceTest.java +++ b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/PluginServiceTest.java @@ -19,7 +19,9 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; @@ -30,12 +32,18 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; import org.apache.hertzbeat.common.constants.PluginType; import org.apache.hertzbeat.common.entity.manager.PluginItem; import org.apache.hertzbeat.common.entity.manager.PluginMetadata; @@ -48,6 +56,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -159,4 +168,29 @@ void testGetPlugins() { verify(metadataDao, times(1)).findAll(any(Specification.class), any(PageRequest.class)); } + @Test + void testZipSlipDetected(@TempDir File tempDir) throws Exception { + File maliciousJar = new File(tempDir, "malicious.jar"); + try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(maliciousJar))) { + JarEntry evilEntry = new JarEntry("../../evil.jar"); + jos.putNextEntry(evilEntry); + jos.write("malicious-content".getBytes(StandardCharsets.UTF_8)); + jos.closeEntry(); + } + Method method = PluginServiceImpl.class.getDeclaredMethod( + "loadLibInPlugin", String.class, Long.class); + method.setAccessible(true); + + Exception exception = assertThrows(Exception.class, () -> { + try { + method.invoke(pluginService, maliciousJar.getAbsolutePath(), 1L); + } catch (java.lang.reflect.InvocationTargetException e) { + throw e.getCause(); + } + }); + + assertInstanceOf(IOException.class, exception); + assertTrue(exception.getMessage().contains("Zip Slip detected")); + } + }