From 8622935bdf2188f9b88b20ad2f917936ff0cb5e4 Mon Sep 17 00:00:00 2001 From: sunnylqm Date: Tue, 12 Dec 2023 22:59:32 +0800 Subject: [PATCH] fix: zipslip --- .../modules/update/DownloadTask.java | 48 ++++---------- .../modules/update/SafeZipFile.java | 63 +++++++++++-------- 2 files changed, 49 insertions(+), 62 deletions(-) diff --git a/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java b/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java index 2bc8188..543cb12 100644 --- a/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +++ b/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java @@ -237,19 +237,7 @@ class DownloadTask extends AsyncTask { while (entries.hasMoreElements()) { ZipEntry ze = entries.nextElement(); - String fn = ze.getName(); - File fmd = new File(param.unzipDirectory, fn); - - if (UpdateContext.DEBUG) { - Log.d("RNUpdate", "Unzipping " + fn); - } - - if (ze.isDirectory()) { - fmd.mkdirs(); - continue; - } - - zipFile.unzipToFile(ze, fmd); + zipFile.unzipToPath(ze, param.unzipDirectory); } zipFile.close(); @@ -324,8 +312,15 @@ class DownloadTask extends AsyncTask { } else { target = copyList.get((from)); } - target.add(new File(param.unzipDirectory, to)); - //copyFromResource(from, new File(param.unzipDirectory, to)); + File toFile = new File(param.unzipDirectory, to); + + // Fixing a Zip Path Traversal Vulnerability + // https://support.google.com/faqs/answer/9294009 + String canonicalPath = toFile.getCanonicalPath(); + if (!canonicalPath.startsWith(param.unzipDirectory.getCanonicalPath() + File.separator)) { + throw new SecurityException("Illegal name: " + to); + } + target.add(toFile); } continue; } @@ -339,18 +334,9 @@ class DownloadTask extends AsyncTask { fout.close(); continue; } - File fmd = new File(param.unzipDirectory, fn); - if (UpdateContext.DEBUG) { - Log.d("RNUpdate", "Unzipping " + fn); - } - if (ze.isDirectory()) { - fmd.mkdirs(); - continue; - } - - zipFile.unzipToFile(ze, fmd); + zipFile.unzipToPath(ze, param.unzipDirectory); } zipFile.close(); @@ -419,18 +405,8 @@ class DownloadTask extends AsyncTask { fout.close(); continue; } - File fmd = new File(param.unzipDirectory, fn); - if (UpdateContext.DEBUG) { - Log.d("RNUpdate", "Unzipping " + fn); - } - - if (ze.isDirectory()) { - fmd.mkdirs(); - continue; - } - - zipFile.unzipToFile(ze, fmd); + zipFile.unzipToPath(ze, param.unzipDirectory); } zipFile.close(); diff --git a/android/src/main/java/cn/reactnative/modules/update/SafeZipFile.java b/android/src/main/java/cn/reactnative/modules/update/SafeZipFile.java index bed95e9..bbcf6ab 100644 --- a/android/src/main/java/cn/reactnative/modules/update/SafeZipFile.java +++ b/android/src/main/java/cn/reactnative/modules/update/SafeZipFile.java @@ -1,5 +1,7 @@ package cn.reactnative.modules.update; +import android.util.Log; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; @@ -10,12 +12,15 @@ import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; + public class SafeZipFile extends ZipFile { public SafeZipFile(File file) throws IOException { super(file); } + private static final int BUFFER_SIZE = 8192; + @Override public Enumeration entries() { return new SafeZipEntryIterator(super.entries()); @@ -43,40 +48,46 @@ public class SafeZipFile extends ZipFile { * avoid ZipperDown */ if (null != name && (name.contains("../") || name.contains("..\\"))) { - throw new SecurityException("illegal entry: " + entry.getName()); + throw new SecurityException("illegal entry: " + name); } } return entry; } } - public void unzipToFile(ZipEntry entry, File output) throws IOException { - InputStream inputStream = null; - try { - inputStream = getInputStream(entry); - writeOutInputStream(output, inputStream); - } finally { - if (inputStream != null) { - inputStream.close(); + public void unzipToPath(ZipEntry ze, File targetPath) throws IOException { + String name = ze.getName(); + File target = new File(targetPath, name); + + // Fixing a Zip Path Traversal Vulnerability + // https://support.google.com/faqs/answer/9294009 + String canonicalPath = target.getCanonicalPath(); + if (!canonicalPath.startsWith(targetPath.getCanonicalPath() + File.separator)) { + throw new SecurityException("Illegal name: " + name); + } + + if (UpdateContext.DEBUG) { + Log.d("RNUpdate", "Unzipping " + name); + } + + if (ze.isDirectory()) { + target.mkdirs(); + return; + } + unzipToFile(ze, target); + } + + public void unzipToFile(ZipEntry ze, File target) throws IOException { + try (InputStream inputStream = getInputStream(ze)) { + try (BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(target)); + BufferedInputStream input = new BufferedInputStream(inputStream)) { + byte[] buffer = new byte[BUFFER_SIZE]; + int n; + while ((n = input.read(buffer, 0, BUFFER_SIZE)) >= 0) { + output.write(buffer, 0, n); + } } } } - private void writeOutInputStream(File file, InputStream inputStream) throws IOException { - BufferedOutputStream output = null; - try { - output = new BufferedOutputStream( - new FileOutputStream(file)); - BufferedInputStream input = new BufferedInputStream(inputStream); - byte b[] = new byte[8192]; - int n; - while ((n = input.read(b, 0, 8192)) >= 0) { - output.write(b, 0, n); - } - } finally { - if (output != null) { - output.close(); - } - } - } } \ No newline at end of file