Firstly, the necessary condition for the issue to occur is that the project is set with a targetSdkVersion of 34. The same code, with a targetSdkVersion of 33, does not encounter the issue on the same Android 14 device.
My code is as follows:
fun unzip(
sourceFile: File, targetDirPath: String,
onSuccess: (() -> Unit)?, onError: ((msg: String?) -> Unit)?
) {
try {
var sumLength: Long = 0
val zis = ZipInputStream(
BufferedInputStream(
FileInputStream(sourceFile)
)
)
zis.use {
var ze: ZipEntry?
var count: Int
val buffer = ByteArray(8192)
while (zis.nextEntry.also { ze = it } != null) {
val file = File(targetDirPath, ze!!.name)
val dir: File = if (ze!!.isDirectory) file else file.parentFile
if (!dir.isDirectory && !dir.mkdirs()) {
throw FileNotFoundException(
"Failed to ensure directory: " +
dir.absolutePath
)
}
if (ze!!.isDirectory) {
continue
}
val fout = FileOutputStream(file)
fout.use {
while (zis.read(buffer).also { count = it } != -1) {
sumLength += count.toLong()
fout.write(buffer, 0, count)
fout.flush()
}
}
}
}
onSuccess?.invoke()
} catch (e: Exception) {
onError?.invoke(e.message)
}
}
I've looked into the official documentation and haven't found any specific handling required for unzip operations when upgrading from targetSdkVersion 33 to targetSdkVersion 34.
Detailed stack error is as follows:
14:56:54.757 7949-7949 System.err com....aes W java.util.zip.ZipException: Invalid zip entry path: /xxx
14:56:54.761 7949-7949 System.err com....aes W at com.android.internal.os.SafeZipPathValidatorCallback.onZipEntryAccess(SafeZipPathValidatorCallback.java:52)
14:56:54.761 7949-7949 System.err com....aes W at java.util.zip.ZipInputStream.readLOC(ZipInputStream.java:335)
14:56:54.761 7949-7949 System.err com....aes W at java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:135)
Does anyone know what caused this? Thank you very much.
I found the issue and also identified the solution. https://developer.android.com/about/versions/14/behavior-changes-14
For apps targeting Android 14, Android prevents the Zip Path Traversal Vulnerability in the following way: ZipFile(String) and ZipInputStream.getNextEntry() throws a ZipException if zip file entry names contain ".." or start with "/".
Apps can opt-out from this validation by calling dalvik.system.ZipPathValidator.clearCallback().
So, there are two possible solutions: either reprocess your zip package or implement the code to disable the validation:
if (Build.VERSION.SDK_INT >= 34) {
ZipPathValidator.clearCallback()
}
Additionally, you can also listen to the relevant callbacks.
if (Build.VERSION.SDK_INT >= 34) {
ZipPathValidator.setCallback(object : ZipPathValidator.Callback {
override fun onZipEntryAccess(path: String) {
// print log...
}
})
}