Refer:
https://github.com/sbt/io/commit/124538348db0713c80793cb57b915f97ec13188a
https://build.opensuse.org/projects/SUSE:SLE-15-SP2:Update/packages/sbt/files/sbt-CVE-2023-46122.patch?expand=1

From f928cdd8aebc5a2b85c326cc267e698229e0b7b2 Mon Sep 17 00:00:00 2001
From: Eugene Yokota <eed3si9n@gmail.com>
Date: Sun, 22 Oct 2023 04:42:16 -0400
Subject: [PATCH] Fixes zip-slip vulnerability

Fixes https://github.com/sbt/io/issues/358
Ref codehaus-plexus/plexus-archiver 87

**Problem**
IO.unzip currently has zip-slip vulnerability, which can write arbitrary
files on the machine using specially crafted zip archive that holds path
traversal file names.

**Solution**
This replicates the fix originally sent to plex-archiver by Snyk Team.

---
 util/io/src/main/scala/sbt/IO.scala | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/util/io/src/main/scala/sbt/IO.scala b/util/io/src/main/scala/sbt/IO.scala
index ed97657..f09d561 100644
--- a/util/io/src/main/scala/sbt/IO.scala
+++ b/util/io/src/main/scala/sbt/IO.scala
@@ -10,6 +10,7 @@ import java.io.{BufferedReader, ByteArrayOutputStream, BufferedWriter, File, Fil
 import java.io.{ObjectInputStream, ObjectStreamClass}
 import java.net.{URI, URISyntaxException, URL}
 import java.nio.charset.Charset
+import java.nio.file.{ Path => NioPath, _ }
 import java.util.Properties
 import java.util.jar.{Attributes, JarEntry, JarFile, JarInputStream, JarOutputStream, Manifest}
 import java.util.zip.{CRC32, GZIPOutputStream, ZipEntry, ZipFile, ZipInputStream, ZipOutputStream}
@@ -190,11 +191,16 @@ object IO
 	def unzipStream(from: InputStream, toDirectory: File, filter: NameFilter = AllPassFilter, preserveLastModified: Boolean = true): Set[File] =
 	{
 		createDirectory(toDirectory)
-		zipInputStream(from) { zipInput => extract(zipInput, toDirectory, filter, preserveLastModified) }
+                zipInputStream(from) { zipInput => extract(zipInput, toDirectory.toPath, filter, preserveLastModified) }
 	}
-	private def extract(from: ZipInputStream, toDirectory: File, filter: NameFilter, preserveLastModified: Boolean) =
+        private def extract(from: ZipInputStream, toDirectory: NioPath, filter: NameFilter, preserveLastModified: Boolean) =
 	{
-		val set = new HashSet[File]
+                val set = new HashSet[NioPath]
+                val canonicalDirPath = toDirectory.normalize().toString
+                def validateExtractPath(name: String, target: NioPath): Unit =
+                  if (!target.normalize().toString.startsWith(canonicalDirPath)) {
+                    throw new RuntimeException(s"Entry ($name) is outside of the target directory")
+                  }
 		def next()
 		{
 			val entry = from.getNextEntry
@@ -205,19 +211,20 @@ object IO
 				val name = entry.getName
 				if(filter.accept(name))
 				{
-					val target = new File(toDirectory, name)
+                                        val target = toDirectory.resolve(name)
+                                        validateExtractPath(name, target)
 					//log.debug("Extracting zip entry '" + name + "' to '" + target + "'")
 					if(entry.isDirectory)
-						createDirectory(target)
+                                                createDirectory(target.toFile)
 					else
 					{
 						set += target
 						translate("Error extracting zip entry '" + name + "' to '" + target + "': ") {
-							fileOutputStream(false)(target) { out => transfer(from, out) }
+                                                        fileOutputStream(false)(target.toFile) { out => transfer(from, out) }
 						}
 					}
 					if(preserveLastModified)
-						target.setLastModified(entry.getTime)
+                                                target.toFile.setLastModified(entry.getTime)
 				}
 				else
 				{
@@ -228,7 +235,7 @@ object IO
 			}
 		}
 		next()
-		Set() ++ set
+                (Set() ++ set).map(_.toFile)
 	}
 
 	/** Retrieves the content of the given URL and writes it to the given File. */
-- 
2.43.0

