From 734502b89545b11b4c03c7eddb0ec07608368084 Mon Sep 17 00:00:00 2001 From: Duansg Date: Sun, 15 Mar 2026 23:54:13 +0800 Subject: [PATCH 1/3] [improve] improve plugin upload validation --- .../hertzbeat/manager/service/impl/PluginServiceImpl.java | 4 ++++ 1 file changed, 4 insertions(+) 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; } From 1dd9dd15c353467a12e9b604ba5ac19e8b87b8b7 Mon Sep 17 00:00:00 2001 From: Duansg Date: Mon, 16 Mar 2026 00:02:52 +0800 Subject: [PATCH 2/3] add test unit --- .../manager/service/PluginServiceTest.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) 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")); + } + } From de0bd51bac0117734e3daef04f8f875223620dab Mon Sep 17 00:00:00 2001 From: Duansg Date: Mon, 16 Mar 2026 00:19:31 +0800 Subject: [PATCH 3/3] reporting coverage does not affect the main workflow --- .github/workflows/backend-build-test.yml | 1 + 1 file changed, 1 insertion(+) 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