This is an automated email from the ASF dual-hosted git repository.

rzo1 pushed a commit to branch tomee-10.x
in repository https://gitbox.apache.org/repos/asf/tomee.git

commit 7b9ed6ba3e9bf333957987093066b0cacc291373
Author: Richard Zowalla <[email protected]>
AuthorDate: Tue Oct 13 08:49:07 2026 +0200

    Reject archive entries escaping the target in tomee-maven-plugin (#3079)
    
    reject archive entries resolving outside catalina base, as Zips does
    
    (cherry picked from commit 8fb8e89deed464d308d24164045b66d2d1a57440)
---
 .../openejb/maven/plugin/AbstractTomEEMojo.java    |   9 ++
 .../org/apache/openejb/maven/plugin/UnzipTest.java | 104 +++++++++++++++++++++
 2 files changed, 113 insertions(+)

diff --git 
a/maven/tomee-maven-plugin/src/main/java/org/apache/openejb/maven/plugin/AbstractTomEEMojo.java
 
b/maven/tomee-maven-plugin/src/main/java/org/apache/openejb/maven/plugin/AbstractTomEEMojo.java
index 9570ba839c..98535a888d 100644
--- 
a/maven/tomee-maven-plugin/src/main/java/org/apache/openejb/maven/plugin/AbstractTomEEMojo.java
+++ 
b/maven/tomee-maven-plugin/src/main/java/org/apache/openejb/maven/plugin/AbstractTomEEMojo.java
@@ -1751,6 +1751,8 @@ public abstract class AbstractTomEEMojo extends 
AbstractAddressMojo {
 
     private void unzip(final File mvnTomEE) {
         try (ZipFile in = new ZipFile(mvnTomEE)) {
+            final String destinationPath = catalinaBase.getCanonicalPath();
+            final String destinationPrefix = 
destinationPath.endsWith(File.separator) ? destinationPath : destinationPath + 
File.separator;
 
             final Enumeration<? extends ZipEntry> entries = in.entries();
             while (entries.hasMoreElements()) {
@@ -1767,6 +1769,13 @@ public abstract class AbstractTomEEMojo extends 
AbstractAddressMojo {
                     name = name.substring(idx + 1);
                 }
                 final File dest = new File(catalinaBase.getAbsolutePath(), 
name);
+
+                // only extract entries resolving inside the destination 
directory
+                final String destPath = dest.getCanonicalPath();
+                if (!destPath.equals(destinationPath) && 
!destPath.startsWith(destinationPrefix)) {
+                    throw new IOException("Zip entry escapes the destination 
directory: " + entry.getName());
+                }
+
                 if (!dest.exists()) {
                     final File parent = dest.getParentFile();
                     if ((!parent.exists() && !parent.mkdirs())
diff --git 
a/maven/tomee-maven-plugin/src/test/java/org/apache/openejb/maven/plugin/UnzipTest.java
 
b/maven/tomee-maven-plugin/src/test/java/org/apache/openejb/maven/plugin/UnzipTest.java
new file mode 100644
index 0000000000..aa91da8e87
--- /dev/null
+++ 
b/maven/tomee-maven-plugin/src/test/java/org/apache/openejb/maven/plugin/UnzipTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.openejb.maven.plugin;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class UnzipTest {
+    @Rule
+    public final TemporaryFolder folder = new TemporaryFolder();
+
+    @Test
+    public void rejectsEntryEscapingCatalinaBase() throws Exception {
+        final File catalinaBase = folder.newFolder("base");
+        final File zip = zip(folder.newFile("evil.zip"), "../evil.txt");
+
+        try {
+            unzip(mojo(catalinaBase, false), zip);
+            fail("entry escaping catalina base must be rejected");
+        } catch (final InvocationTargetException e) {
+            assertTrue(String.valueOf(e.getCause()), e.getCause() instanceof 
TomEEException);
+        }
+        assertFalse(new File(folder.getRoot(), "evil.txt").exists());
+    }
+
+    @Test
+    public void rejectsEntryEscapingCatalinaBaseAfterRootFolderSkip() throws 
Exception {
+        final File catalinaBase = folder.newFolder("work", "base");
+        final File zip = zip(folder.newFile("evil.zip"), 
"apache-tomee/../../evil.txt");
+
+        try {
+            unzip(mojo(catalinaBase, true), zip);
+            fail("entry escaping catalina base must be rejected");
+        } catch (final InvocationTargetException e) {
+            assertTrue(String.valueOf(e.getCause()), e.getCause() instanceof 
TomEEException);
+        }
+        assertFalse(new File(folder.getRoot(), "evil.txt").exists());
+    }
+
+    @Test
+    public void extractsRegularEntries() throws Exception {
+        final File catalinaBase = folder.newFolder("base");
+        final File zip = zip(folder.newFile("dist.zip"), 
"apache-tomee/conf/marker.txt");
+
+        unzip(mojo(catalinaBase, true), zip);
+
+        assertTrue(new File(catalinaBase, "conf/marker.txt").isFile());
+    }
+
+    private static AbstractTomEEMojo mojo(final File catalinaBase, final 
boolean skipRootFolder) {
+        final AbstractTomEEMojo mojo = new AbstractTomEEMojo() {
+            @Override
+            public String getCmd() {
+                return "test";
+            }
+        };
+        mojo.catalinaBase = catalinaBase;
+        mojo.skipRootFolderOnUnzip = skipRootFolder;
+        mojo.overrideOnUnzip = true;
+        return mojo;
+    }
+
+    private static void unzip(final AbstractTomEEMojo mojo, final File zip) 
throws Exception {
+        final Method unzip = 
AbstractTomEEMojo.class.getDeclaredMethod("unzip", File.class);
+        unzip.setAccessible(true);
+        unzip.invoke(mojo, zip);
+    }
+
+    private static File zip(final File target, final String entryName) throws 
Exception {
+        try (ZipOutputStream out = new ZipOutputStream(new 
FileOutputStream(target))) {
+            out.putNextEntry(new ZipEntry(entryName));
+            out.write("content".getBytes(StandardCharsets.UTF_8));
+            out.closeEntry();
+        }
+        return target;
+    }
+}

Reply via email to