diff --git a/SPECS/javapackages-bootstrap/CVE-2025-67030.patch b/SPECS/javapackages-bootstrap/CVE-2025-67030.patch new file mode 100644 index 00000000000..1c36072668b --- /dev/null +++ b/SPECS/javapackages-bootstrap/CVE-2025-67030.patch @@ -0,0 +1,201 @@ +From: Copilot <198982749+Copilot@users.noreply.github.com> +Date: Sun, 9 Nov 2025 12:32:38 +0100 +Subject: [PATCH] Fix Zip Slip vulnerability in archive extraction (#296) + +--------- +Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> +Co-authored-by: slachiewicz <6705942+slachiewicz@users.noreply.github.com> + +Upstream Patch Reference: https://github.com/codehaus-plexus/plexus-utils/commit/6d780b3378829318ba5c2d29547e0012d5b29642.patch +--- + .../java/org/codehaus/plexus/util/Expand.java | 18 ++- + .../org/codehaus/plexus/util/ExpandTest.java | 148 ++++++++++++++++++ + 2 files changed, 162 insertions(+), 4 deletions(-) + create mode 100644 src/test/java/org/codehaus/plexus/util/ExpandTest.java + +diff --git a/src/main/java/org/codehaus/plexus/util/Expand.java b/src/main/java/org/codehaus/plexus/util/Expand.java +index 3d3e98a..512ee83 100644 +--- a/src/main/java/org/codehaus/plexus/util/Expand.java ++++ b/src/main/java/org/codehaus/plexus/util/Expand.java +@@ -136,10 +136,20 @@ protected void extractFile( File srcF, File dir, InputStream compressedInputStre + { + File f = FileUtils.resolveFile( dir, entryName ); + +- if ( !f.getAbsolutePath().startsWith( dir.getAbsolutePath() ) ) +- { +- throw new IOException( "Entry '" + entryName + "' outside the target directory." ); +- } ++ try { ++ String canonicalDirPath = dir.getCanonicalPath(); ++ String canonicalFilePath = f.getCanonicalPath(); ++ ++ // Ensure the file is within the target directory ++ // We need to check that the canonical file path starts with the canonical directory path ++ // followed by a file separator to prevent path traversal attacks ++ if (!canonicalFilePath.startsWith(canonicalDirPath + File.separator) ++ && !canonicalFilePath.equals(canonicalDirPath)) { ++ throw new IOException("Entry '" + entryName + "' outside the target directory."); ++ } ++ } catch (IOException e) { ++ throw new IOException("Failed to verify entry path for '" + entryName + "'", e); ++ } + + try + { +diff --git a/src/test/java/org/codehaus/plexus/util/ExpandTest.java b/src/test/java/org/codehaus/plexus/util/ExpandTest.java +new file mode 100644 +index 0000000..46e3d0e +--- /dev/null ++++ b/src/test/java/org/codehaus/plexus/util/ExpandTest.java +@@ -0,0 +1,148 @@ ++package org.codehaus.plexus.util; ++ ++/* ++ * Copyright The Codehaus Foundation. ++ * ++ * Licensed 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 java.io.File; ++import java.nio.file.Files; ++import java.util.zip.ZipEntry; ++import java.util.zip.ZipOutputStream; ++ ++import org.junit.jupiter.api.Test; ++ ++import static org.junit.jupiter.api.Assertions.assertFalse; ++import static org.junit.jupiter.api.Assertions.assertThrows; ++import static org.junit.jupiter.api.Assertions.assertTrue; ++ ++/** ++ * Test for {@link Expand}. ++ */ ++class ExpandTest extends FileBasedTestCase { ++ ++ @Test ++ void testZipSlipVulnerabilityWithParentDirectory() throws Exception { ++ File tempDir = getTestDirectory(); ++ File zipFile = new File(tempDir, "malicious.zip"); ++ File targetDir = new File(tempDir, "extract"); ++ targetDir.mkdirs(); ++ ++ // Create a malicious zip with path traversal ++ try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zipFile.toPath()))) { ++ ZipEntry entry = new ZipEntry("../../evil.txt"); ++ zos.putNextEntry(entry); ++ zos.write("malicious content".getBytes()); ++ zos.closeEntry(); ++ } ++ ++ Expand expand = new Expand(); ++ expand.setSrc(zipFile); ++ expand.setDest(targetDir); ++ ++ // This should throw an exception, not extract the file ++ assertThrows(Exception.class, () -> expand.execute()); ++ ++ // Verify the file was not created outside the target directory ++ File evilFile = new File(tempDir, "evil.txt"); ++ assertFalse(evilFile.exists(), "File should not be extracted outside target directory"); ++ } ++ ++ @Test ++ void testZipSlipVulnerabilityWithAbsolutePath() throws Exception { ++ File tempDir = getTestDirectory(); ++ File zipFile = new File(tempDir, "malicious-absolute.zip"); ++ File targetDir = new File(tempDir, "extract-abs"); ++ targetDir.mkdirs(); ++ ++ // Create a malicious zip with absolute path ++ File evilTarget = new File("/tmp/evil-absolute.txt"); ++ try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zipFile.toPath()))) { ++ ZipEntry entry = new ZipEntry(evilTarget.getAbsolutePath()); ++ zos.putNextEntry(entry); ++ zos.write("malicious content".getBytes()); ++ zos.closeEntry(); ++ } ++ ++ Expand expand = new Expand(); ++ expand.setSrc(zipFile); ++ expand.setDest(targetDir); ++ ++ // This should throw an exception, not extract the file ++ assertThrows(Exception.class, () -> expand.execute()); ++ ++ // Verify the file was not created at the absolute path ++ assertFalse(evilTarget.exists(), "File should not be extracted to absolute path"); ++ } ++ ++ @Test ++ void testZipSlipVulnerabilityWithSimilarDirectoryName() throws Exception { ++ File tempDir = getTestDirectory(); ++ File zipFile = new File(tempDir, "malicious-similar.zip"); ++ File targetDir = new File(tempDir, "extract"); ++ targetDir.mkdirs(); ++ ++ // Create a directory with a similar name to test prefix matching vulnerability ++ File similarDir = new File(tempDir, "extract-evil"); ++ similarDir.mkdirs(); ++ ++ // Create a malicious zip that tries to exploit prefix matching ++ // If targetDir is /tmp/extract, this tries to write to /tmp/extract-evil/file.txt ++ String maliciousPath = "../extract-evil/evil.txt"; ++ try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zipFile.toPath()))) { ++ ZipEntry entry = new ZipEntry(maliciousPath); ++ zos.putNextEntry(entry); ++ zos.write("malicious content".getBytes()); ++ zos.closeEntry(); ++ } ++ ++ Expand expand = new Expand(); ++ expand.setSrc(zipFile); ++ expand.setDest(targetDir); ++ ++ // This should throw an exception, not extract the file ++ assertThrows(Exception.class, () -> expand.execute()); ++ ++ // Verify the file was not created in the similar directory ++ File evilFile = new File(similarDir, "evil.txt"); ++ assertFalse(evilFile.exists(), "File should not be extracted to directory with similar name"); ++ } ++ ++ @Test ++ void testNormalZipExtraction() throws Exception { ++ File tempDir = getTestDirectory(); ++ File zipFile = new File(tempDir, "normal.zip"); ++ File targetDir = new File(tempDir, "extract-normal"); ++ targetDir.mkdirs(); ++ ++ // Create a normal zip ++ try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zipFile.toPath()))) { ++ ZipEntry entry = new ZipEntry("subdir/normal.txt"); ++ zos.putNextEntry(entry); ++ zos.write("normal content".getBytes()); ++ zos.closeEntry(); ++ } ++ ++ Expand expand = new Expand(); ++ expand.setSrc(zipFile); ++ expand.setDest(targetDir); ++ ++ // This should succeed ++ expand.execute(); ++ ++ // Verify the file was created in the correct location ++ File normalFile = new File(targetDir, "subdir/normal.txt"); ++ assertTrue(normalFile.exists(), "File should be extracted to correct location"); ++ } ++} +-- +2.45.4 + diff --git a/SPECS/javapackages-bootstrap/javapackages-bootstrap.spec b/SPECS/javapackages-bootstrap/javapackages-bootstrap.spec index 0c6d75545e8..377c2d724f6 100644 --- a/SPECS/javapackages-bootstrap/javapackages-bootstrap.spec +++ b/SPECS/javapackages-bootstrap/javapackages-bootstrap.spec @@ -13,7 +13,7 @@ Name: javapackages-bootstrap Version: 1.5.0 -Release: 7%{?dist} +Release: 8%{?dist} Summary: A means of bootstrapping Java Packages Tools # For detailed info see the file javapackages-bootstrap-PACKAGE-LICENSING License: ASL 2.0 and ASL 1.1 and (ASL 2.0 or EPL-2.0) and (EPL-2.0 or GPLv2 with exceptions) and MIT and (BSD with advertising) and BSD-3-Clause and EPL-1.0 and EPL-2.0 and CDDL-1.0 and xpp and CC0 and Public Domain @@ -141,6 +141,7 @@ Patch1: 0001-Remove-usage-of-ArchiveStreamFactory.patch Patch2: CVE-2023-37460.patch Patch3: Internal-Java-API.patch Patch4: CVE-2021-36373.patch +Patch5: CVE-2025-67030.patch Provides: bundled(ant) = 1.10.9 Provides: bundled(apache-parent) = 23 @@ -305,6 +306,10 @@ pushd "downstream/ant" %patch4 -p1 popd +pushd "downstream/plexus-utils" +%patch5 -p1 +popd + # remove guava.xml from javapackage-bootstrap 1.5.0 # import guava.xml 32.1.3 from Fedora 40 # edit version from guava.properties @@ -393,6 +398,9 @@ sed -i 's|/usr/lib/jvm/java-11-openjdk|%{java_home}|' %{buildroot}%{launchersPat %doc AUTHORS %changelog +* Fri Apr 03 2026 Akhila Guruju - 1.5.0-8 +- Patch CVE-2025-67030 + * Thu May 15 2025 Andrew Phelps - 1.5.0-7 - Attempt incremental build twice to avoid random Dependency cycle errors