androidzipinputstream

The getNextEntry method of ZipInputStream throws a ZipException: Invalid zip entry path: /xxxx error when running on an Android 14 device


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.


Solution

  • 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...
            }
        })
    }