From 1a3856d4bba4c9ba1d30c372a472309c061ce973 Mon Sep 17 00:00:00 2001
From: "pixeebot[bot]" <104101892+pixeebot[bot]@users.noreply.github.com>
Date: Sun, 10 Aug 2025 03:54:50 +0000
Subject: [PATCH] Introduced protections against "zip slip" attacks
---
.../core/lms/ZipFileBackedMessageTest.java | 3 +-
.../aggregator/ZipAggregatorTest.java | 361 +++++++++---------
2 files changed, 183 insertions(+), 181 deletions(-)
diff --git a/interlok-core/src/test/java/com/adaptris/core/lms/ZipFileBackedMessageTest.java b/interlok-core/src/test/java/com/adaptris/core/lms/ZipFileBackedMessageTest.java
index 47cdb9fe6..f08d84b3e 100644
--- a/interlok-core/src/test/java/com/adaptris/core/lms/ZipFileBackedMessageTest.java
+++ b/interlok-core/src/test/java/com/adaptris/core/lms/ZipFileBackedMessageTest.java
@@ -16,6 +16,7 @@
package com.adaptris.core.lms;
+import io.github.pixee.security.ZipSecurity;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotSame;
@@ -188,7 +189,7 @@ public void testCreateCompressedFile() throws Exception {
}
// Check if the InputStream from the message also yields compressed data
- try (ZipInputStream zin = new ZipInputStream(orig.getInputStream());
+ try (ZipInputStream zin = ZipSecurity.createHardenedInputStream(orig.getInputStream());
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
zin.getNextEntry();
IOUtils.copy(zin, out);
diff --git a/interlok-core/src/test/java/com/adaptris/core/services/aggregator/ZipAggregatorTest.java b/interlok-core/src/test/java/com/adaptris/core/services/aggregator/ZipAggregatorTest.java
index be7460bbc..700f0da08 100644
--- a/interlok-core/src/test/java/com/adaptris/core/services/aggregator/ZipAggregatorTest.java
+++ b/interlok-core/src/test/java/com/adaptris/core/services/aggregator/ZipAggregatorTest.java
@@ -1,180 +1,181 @@
-package com.adaptris.core.services.aggregator;
-
-import static com.adaptris.core.services.aggregator.ZipAggregator.DEFAULT_FILENAME_METADATA;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.io.ByteArrayInputStream;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import com.adaptris.core.AdaptrisMessage;
-import com.adaptris.core.AdaptrisMessageFactory;
-import com.adaptris.core.CoreException;
-import com.adaptris.core.Service;
-import com.adaptris.core.services.conditional.conditions.ConditionImpl;
-import com.adaptris.core.services.metadata.AddFormattedMetadataService;
-import com.adaptris.core.services.splitter.SplitByMetadata;
-import com.adaptris.core.stubs.DefectiveMessageFactory;
-import com.adaptris.core.stubs.DefectiveMessageFactory.WhenToBreak;
-
-public class ZipAggregatorTest extends AggregatingServiceExample {
-
- @Test
- public void testJoinMessage() throws Exception {
- ZipAggregator aggr = new ZipAggregator();
- AdaptrisMessageFactory fac = AdaptrisMessageFactory.getDefaultInstance();
-
- AdaptrisMessage original = fac.newMessage();
- AdaptrisMessage splitMsg1 = fac.newMessage("hello");
- splitMsg1.addMetadata(DEFAULT_FILENAME_METADATA, "file1.xml");
- AdaptrisMessage splitMsg2 = fac.newMessage("world");
- splitMsg2.addMetadata(DEFAULT_FILENAME_METADATA, "file2.xml");
- AdaptrisMessage willBeIgnoredMsg = fac.newMessage("world");
- aggr.joinMessage(original, Arrays.asList(splitMsg1, splitMsg2, willBeIgnoredMsg));
-
- boolean isZipped = new ZipInputStream(new ByteArrayInputStream(original.getPayload())).getNextEntry() != null;
-
- assertTrue(isZipped);
-
- Map results = zipBytesToResultsMap(original.getPayload());
-
- assertEquals(2, results.size());
- assertTrue(results.containsKey("file1.xml"));
- assertTrue(results.containsKey("file2.xml"));
- assertEquals(results.get("file1.xml"), "hello");
- assertEquals(results.get("file2.xml"), "world");
- }
-
- @Test
- public void testJoinMessageWithFilter() throws Exception {
- ZipAggregator aggr = new ZipAggregator();
- aggr.setFilterCondition(new MetadataFilenameCondition());
- AdaptrisMessage original = AdaptrisMessageFactory.getDefaultInstance().newMessage();
-
- AdaptrisMessageFactory fac = AdaptrisMessageFactory.getDefaultInstance();
- AdaptrisMessage splitMsg1 = fac.newMessage("hello");
- splitMsg1.addMetadata(DEFAULT_FILENAME_METADATA, "xfile1.xml");
- AdaptrisMessage splitMsg2 = fac.newMessage("world2");
- splitMsg2.addMetadata(DEFAULT_FILENAME_METADATA, "file2.xml");
- AdaptrisMessage splitMsg3 = fac.newMessage("world3");
- splitMsg3.addMetadata(DEFAULT_FILENAME_METADATA, "xfile3.xml");
- AdaptrisMessage splitMsg4 = fac.newMessage("world4");
- splitMsg4.addMetadata(DEFAULT_FILENAME_METADATA, "file4.xml");
- AdaptrisMessage willBeIgnoredMsg = fac.newMessage("world4");
- aggr.joinMessage(original, Arrays.asList(splitMsg1, splitMsg2, splitMsg3, splitMsg4, willBeIgnoredMsg));
-
- boolean isZipped = new ZipInputStream(new ByteArrayInputStream(original.getPayload())).getNextEntry() != null;
-
- assertTrue(isZipped);
-
- Map results = zipBytesToResultsMap(original.getPayload());
-
- assertEquals(2, results.size());
- assertTrue(results.containsKey("file2.xml"));
- assertTrue(results.containsKey("file4.xml"));
- assertEquals(results.get("file2.xml"), "world2");
- assertEquals(results.get("file4.xml"), "world4");
-
- }
-
- @Test
- public void testAggregate_BrokenOutput() throws Exception {
- Assertions.assertThrows(CoreException.class, () -> {
- ZipAggregator aggr = new ZipAggregator();
- AdaptrisMessage original = new DefectiveMessageFactory(WhenToBreak.OUTPUT).newMessage();
- AdaptrisMessageFactory fac = AdaptrisMessageFactory.getDefaultInstance();
- AdaptrisMessage splitMsg1 = fac.newMessage("hello");
- splitMsg1.addMetadata(DEFAULT_FILENAME_METADATA, "xfile1.xml");
- AdaptrisMessage splitMsg2 = fac.newMessage("world2");
- splitMsg2.addMetadata(DEFAULT_FILENAME_METADATA, "file2.xml");
- AdaptrisMessage splitMsg3 = fac.newMessage("world3");
- splitMsg3.addMetadata(DEFAULT_FILENAME_METADATA, "xfile3.xml");
- AdaptrisMessage splitMsg4 = fac.newMessage("world4");
- splitMsg4.addMetadata(DEFAULT_FILENAME_METADATA, "file4.xml");
- AdaptrisMessage willBeIgnoredMsg = fac.newMessage("world4");
- aggr.joinMessage(original, Arrays.asList(splitMsg1, splitMsg2, splitMsg3, splitMsg4, willBeIgnoredMsg));
- });
- }
-
- @Override
- protected String getExampleCommentHeader(Object o) {
- return super.getExampleCommentHeader(o) + "\n\n";
- }
-
- @Override
- protected Object retrieveObjectForSampleConfig() {
- return null;
- }
-
- @Override
- protected List retrieveObjectsForSampleConfig() {
- return createExamples(new SplitByMetadata("comma-separated-list", "value"), new ZipAggregator(),
- new AddFormattedMetadataService().withFormatString("%1$s.xml").withMetadataKey("filename")
- .withArgumentMetadataKeys(Collections.singletonList("value")));
- }
-
- @Override
- protected String createBaseFileName(Object object) {
- return super.createBaseFileName(object) + "-ZipAggregator";
- }
-
- /**
- * Returns a Map where filename is key and value is file
- * contents.
- *
- * @param bytes
- * zip bytes
- * @return Map where filename is key and value is file contents
- * @throws Exception
- */
-
- private Map zipBytesToResultsMap(byte[] bytes) throws Exception {
- byte[] buffer = new byte[1024];
-
- Map results = new HashMap<>();
- ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(bytes));
- try {
- ZipEntry ze;
- while ((ze = zis.getNextEntry()) != null) {
- String fileName = ze.getName();
- String contents = "";
- int len;
- while ((len = zis.read(buffer)) > 0) {
- contents += new String(buffer, 0, len);
- }
- results.put(fileName, contents);
- }
- } finally {
- zis.closeEntry();
- zis.close();
- }
- return results;
- }
-
- // Filter out metadata filename values that start with 'x'
- private class MetadataFilenameCondition extends ConditionImpl {
- @Override
- public boolean evaluate(AdaptrisMessage message) throws CoreException {
- final String metadataValue = message.getMetadataValue(DEFAULT_FILENAME_METADATA);
- if (metadataValue != null && metadataValue.startsWith("x"))
- return false;
- return true;
- }
-
- @Override
- public void close() {
- throw new RuntimeException();
- }
- }
-}
\ No newline at end of file
+package com.adaptris.core.services.aggregator;
+
+import static com.adaptris.core.services.aggregator.ZipAggregator.DEFAULT_FILENAME_METADATA;
+import io.github.pixee.security.ZipSecurity;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import com.adaptris.core.AdaptrisMessage;
+import com.adaptris.core.AdaptrisMessageFactory;
+import com.adaptris.core.CoreException;
+import com.adaptris.core.Service;
+import com.adaptris.core.services.conditional.conditions.ConditionImpl;
+import com.adaptris.core.services.metadata.AddFormattedMetadataService;
+import com.adaptris.core.services.splitter.SplitByMetadata;
+import com.adaptris.core.stubs.DefectiveMessageFactory;
+import com.adaptris.core.stubs.DefectiveMessageFactory.WhenToBreak;
+
+public class ZipAggregatorTest extends AggregatingServiceExample {
+
+ @Test
+ public void testJoinMessage() throws Exception {
+ ZipAggregator aggr = new ZipAggregator();
+ AdaptrisMessageFactory fac = AdaptrisMessageFactory.getDefaultInstance();
+
+ AdaptrisMessage original = fac.newMessage();
+ AdaptrisMessage splitMsg1 = fac.newMessage("hello");
+ splitMsg1.addMetadata(DEFAULT_FILENAME_METADATA, "file1.xml");
+ AdaptrisMessage splitMsg2 = fac.newMessage("world");
+ splitMsg2.addMetadata(DEFAULT_FILENAME_METADATA, "file2.xml");
+ AdaptrisMessage willBeIgnoredMsg = fac.newMessage("world");
+ aggr.joinMessage(original, Arrays.asList(splitMsg1, splitMsg2, willBeIgnoredMsg));
+
+ boolean isZipped = new ZipInputStream(new ByteArrayInputStream(original.getPayload())).getNextEntry() != null;
+
+ assertTrue(isZipped);
+
+ Map results = zipBytesToResultsMap(original.getPayload());
+
+ assertEquals(2, results.size());
+ assertTrue(results.containsKey("file1.xml"));
+ assertTrue(results.containsKey("file2.xml"));
+ assertEquals(results.get("file1.xml"), "hello");
+ assertEquals(results.get("file2.xml"), "world");
+ }
+
+ @Test
+ public void testJoinMessageWithFilter() throws Exception {
+ ZipAggregator aggr = new ZipAggregator();
+ aggr.setFilterCondition(new MetadataFilenameCondition());
+ AdaptrisMessage original = AdaptrisMessageFactory.getDefaultInstance().newMessage();
+
+ AdaptrisMessageFactory fac = AdaptrisMessageFactory.getDefaultInstance();
+ AdaptrisMessage splitMsg1 = fac.newMessage("hello");
+ splitMsg1.addMetadata(DEFAULT_FILENAME_METADATA, "xfile1.xml");
+ AdaptrisMessage splitMsg2 = fac.newMessage("world2");
+ splitMsg2.addMetadata(DEFAULT_FILENAME_METADATA, "file2.xml");
+ AdaptrisMessage splitMsg3 = fac.newMessage("world3");
+ splitMsg3.addMetadata(DEFAULT_FILENAME_METADATA, "xfile3.xml");
+ AdaptrisMessage splitMsg4 = fac.newMessage("world4");
+ splitMsg4.addMetadata(DEFAULT_FILENAME_METADATA, "file4.xml");
+ AdaptrisMessage willBeIgnoredMsg = fac.newMessage("world4");
+ aggr.joinMessage(original, Arrays.asList(splitMsg1, splitMsg2, splitMsg3, splitMsg4, willBeIgnoredMsg));
+
+ boolean isZipped = new ZipInputStream(new ByteArrayInputStream(original.getPayload())).getNextEntry() != null;
+
+ assertTrue(isZipped);
+
+ Map results = zipBytesToResultsMap(original.getPayload());
+
+ assertEquals(2, results.size());
+ assertTrue(results.containsKey("file2.xml"));
+ assertTrue(results.containsKey("file4.xml"));
+ assertEquals(results.get("file2.xml"), "world2");
+ assertEquals(results.get("file4.xml"), "world4");
+
+ }
+
+ @Test
+ public void testAggregate_BrokenOutput() throws Exception {
+ Assertions.assertThrows(CoreException.class, () -> {
+ ZipAggregator aggr = new ZipAggregator();
+ AdaptrisMessage original = new DefectiveMessageFactory(WhenToBreak.OUTPUT).newMessage();
+ AdaptrisMessageFactory fac = AdaptrisMessageFactory.getDefaultInstance();
+ AdaptrisMessage splitMsg1 = fac.newMessage("hello");
+ splitMsg1.addMetadata(DEFAULT_FILENAME_METADATA, "xfile1.xml");
+ AdaptrisMessage splitMsg2 = fac.newMessage("world2");
+ splitMsg2.addMetadata(DEFAULT_FILENAME_METADATA, "file2.xml");
+ AdaptrisMessage splitMsg3 = fac.newMessage("world3");
+ splitMsg3.addMetadata(DEFAULT_FILENAME_METADATA, "xfile3.xml");
+ AdaptrisMessage splitMsg4 = fac.newMessage("world4");
+ splitMsg4.addMetadata(DEFAULT_FILENAME_METADATA, "file4.xml");
+ AdaptrisMessage willBeIgnoredMsg = fac.newMessage("world4");
+ aggr.joinMessage(original, Arrays.asList(splitMsg1, splitMsg2, splitMsg3, splitMsg4, willBeIgnoredMsg));
+ });
+ }
+
+ @Override
+ protected String getExampleCommentHeader(Object o) {
+ return super.getExampleCommentHeader(o) + "\n\n";
+ }
+
+ @Override
+ protected Object retrieveObjectForSampleConfig() {
+ return null;
+ }
+
+ @Override
+ protected List retrieveObjectsForSampleConfig() {
+ return createExamples(new SplitByMetadata("comma-separated-list", "value"), new ZipAggregator(),
+ new AddFormattedMetadataService().withFormatString("%1$s.xml").withMetadataKey("filename")
+ .withArgumentMetadataKeys(Collections.singletonList("value")));
+ }
+
+ @Override
+ protected String createBaseFileName(Object object) {
+ return super.createBaseFileName(object) + "-ZipAggregator";
+ }
+
+ /**
+ * Returns a Map where filename is key and value is file
+ * contents.
+ *
+ * @param bytes
+ * zip bytes
+ * @return Map where filename is key and value is file contents
+ * @throws Exception
+ */
+
+ private Map zipBytesToResultsMap(byte[] bytes) throws Exception {
+ byte[] buffer = new byte[1024];
+
+ Map results = new HashMap<>();
+ ZipInputStream zis = ZipSecurity.createHardenedInputStream(new ByteArrayInputStream(bytes));
+ try {
+ ZipEntry ze;
+ while ((ze = zis.getNextEntry()) != null) {
+ String fileName = ze.getName();
+ String contents = "";
+ int len;
+ while ((len = zis.read(buffer)) > 0) {
+ contents += new String(buffer, 0, len);
+ }
+ results.put(fileName, contents);
+ }
+ } finally {
+ zis.closeEntry();
+ zis.close();
+ }
+ return results;
+ }
+
+ // Filter out metadata filename values that start with 'x'
+ private class MetadataFilenameCondition extends ConditionImpl {
+ @Override
+ public boolean evaluate(AdaptrisMessage message) throws CoreException {
+ final String metadataValue = message.getMetadataValue(DEFAULT_FILENAME_METADATA);
+ if (metadataValue != null && metadataValue.startsWith("x"))
+ return false;
+ return true;
+ }
+
+ @Override
+ public void close() {
+ throw new RuntimeException();
+ }
+ }
+}