I am getting the "Zip Path Traversal Vulnerability" alert in Google Play Console.
I followed official Google docs (https://support.google.com/faqs/answer/9294009) to fix it but the alert is still there.
Here is the code that handles unzipping. I have even tested using a zip file that has the vulnerability described and the exception is raised as expected.
What am I missing?
private boolean unpackZip(File zipFile, File outputDirectory, IOnResult<Integer> progress) {
InputStream is;
ZipInputStream zis;
ZipInputStream zisCount;
try {
int totalEntries = 0;
int entryCount = 0;
String filename;
ZipEntry ze;
is = new FileInputStream(zipFile);
zisCount = new ZipInputStream(new BufferedInputStream(is));
while ((ze = zisCount.getNextEntry()) != null) {
// Fixing a Zip Path Traversal Vulnerability
// (https://support.google.com/faqs/answer/9294009)
filename = ze.getName();
File targetFile = new File(outputDirectory, filename);
String targetPath = targetFile.getCanonicalPath();
if (!targetPath.startsWith(outputDirectory.getCanonicalPath())) {
throw new SecurityException("Archive security error");
}
// -----------------------------------------------------------------------------
totalEntries++;
zisCount.closeEntry();
}
zisCount.close();
is = new FileInputStream(zipFile);
zis = new ZipInputStream(new BufferedInputStream(is));
byte[] buffer = new byte[1024];
int count;
while ((ze = zis.getNextEntry()) != null) {
filename = ze.getName();
// Fixing a Zip Path Traversal Vulnerability (https://support.google.com/faqs/answer/9294009)
File targetFile = new File(outputDirectory, filename);
String targetPath = targetFile.getCanonicalPath();
if (!targetPath.startsWith(outputDirectory.getCanonicalPath())) {
throw new SecurityException("Archive security error");
}
// Need to create directories if not exists, or
// it will generate an Exception...
if (ze.isDirectory()) {
File fmd = new File(outputDirectory, filename);
fmd.mkdirs();
continue;
}
File outputFile = new File(outputDirectory, filename);
for( File parentFile = outputFile.getParentFile(); !parentFile.exists(); parentFile = parentFile.getParentFile() )
{
parentFile.mkdir();
}
FileOutputStream fout = new FileOutputStream(outputFile);
Log.d(TAG, "unzipped " + filename);
while ((count = zis.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
fout.close();
zis.closeEntry();
entryCount++;
}
zis.close();
} catch (IOException | SecurityException e) {
Log.e(TAG, "unpackZip", e);
return false;
}
return true;
}
Related
I want to unzip to zip file in same directory,When I try below code it unzipped below target directory not in target folder,how can I unzipped all files in target directory in android
public static void unzip(String zipFilePath, String destDir) {
BufferedOutputStream bufferedOutputStream = null;
FileInputStream fileInputStream;
File dest_file = new File(destDir);
//dest_file.mkdirs(); // creates if destination directory not existed
try {
fileInputStream = new FileInputStream(zipFilePath);
ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(fileInputStream));
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
String zipEntryName = zipEntry.getName();
File file = new File(destDir + zipEntryName);
if (file.exists()) {
} else if (zipEntry.isDirectory()) {
file.mkdirs();
} else {
byte buffer[] = new byte[1024];
FileOutputStream fileOutputStream = new FileOutputStream(file);
bufferedOutputStream = new BufferedOutputStream(fileOutputStream, 1024);
int count;
while ((count = zipInputStream.read(buffer, 0, 1024)) != -1) {
bufferedOutputStream.write(buffer, 0, count);
}
bufferedOutputStream.flush();
bufferedOutputStream.close();
}
}
zipInputStream.close();
} catch (Exception e) {
Log.e("Decompress", "unzip", e);
}
}
I call unzip File
String source = /storage/emulated/0/Android/data/com.kocsistem.pixageoneandroid/files/Contents/widgetContent + "/" + "123.zip";
String target = /storage/emulated/0/Android/data/com.kocsistem.pixageoneandroid/files/Contents/widgetContent ;
Utils.unzip(source, target);
I am downloading zip file from server to internal storage. And after downloading I want to unzip that file in internal storage itself. My file is downloaded to internal storage and I can see that but while unzip I am not able to read it. Below is my unzipfile() method. Can anyone tell where I am going wrong?
public void unzipfile()
{
try {
Log.d("unzipiing","unzipping");
ContextWrapper cw = new ContextWrapper(context);
String name_="foldername"; //Folder name in device android/data/
File directory = cw.getDir(name_, Context.MODE_PRIVATE);
File mypath=new File(directory,"dwnld");
FileOutputStream fout = new FileOutputStream(mypath);
File yourFile = new File(directory,"dwnld.zip");
Log.d("unzipiing","filepath -" + yourFile.getPath());
FileInputStream fin = new FileInputStream(yourFile.getPath());
ZipInputStream zin = new ZipInputStream(fin);
Log.d("unzipiing","zin size -" + zin.available());
// zin.available() give -1 in console log
BufferedInputStream in = new BufferedInputStream(zin);
BufferedOutputStream out = new BufferedOutputStream(fout);
byte b[] = new byte[zin.available()];
int n;
Log.d("unzip","n - " + in.read(b,0,1024));
while ((n = in.read(b,0,1024)) >= 0) {
out.write(b,0,n);
Log.d("unzip byte"," - " + n);
}
out.flush();
out.close();
in.close();
fin.close();
zin.close();
}
catch (Exception e){
}
}
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
use this method for unzip
public void unzip(String _zipFile, String _targetLocation) {
//create target location folder if not exist
dirChecker(_targetLocatioan);
try {
FileInputStream fin = new FileInputStream(_zipFile);
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
//create dir if required while unzipping
if (ze.isDirectory()) {
dirChecker(ze.getName());
} else {
FileOutputStream fout = new FileOutputStream(_targetLocation + ze.getName());
for (int c = zin.read(); c != -1; c = zin.read()) {
fout.write(c);
}
zin.closeEntry();
fout.close();
}
}
zin.close();
} catch (Exception e) {
System.out.println(e);
}
}
check the path
public void dirChecker(String filepath)
{
File file = new File(filePath);
if(file.exists())
//Do something
else
// Do something else.
}
I am unziping a file based in the official documentation and some examples. My current implementation unzip the file in the same directory where is the zip file. I'd like to unzip to a specific directory in my device. How can I do this? The ZipInputStream allow this feature or do I have to unzip and then move the files to the desired folder?
This is my code:
public static boolean unpackZip(String path, String zipname) {
InputStream is;
ZipInputStream zis;
try {
String filename;
is = new FileInputStream(path + zipname);
zis = new ZipInputStream(new BufferedInputStream(is));
ZipEntry ze;
byte[] buffer = new byte[1024];
int count;
while ((ze = zis.getNextEntry()) != null) {
filename = ze.getName();
if (ze.isDirectory()) {
File fmd = new File(path + filename);
fmd.mkdirs();
continue;
}
FileOutputStream fout = new FileOutputStream(path + filename);
while ((count = zis.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
fout.close();
zis.closeEntry();
}
zis.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
You need to create the folder by splitting the string:
if (filename.contains("/")){
String[] folders = filename.split("/");
for (String item : folders)
{
File fmd = new File(path + item);
if (!item.contains(".") && !fmd.exists()){
fmd.mkdirs();
Log.d("created folder", item);
}
}
}
FULL CODE:
public static boolean unpackZip(String path, String zipname) {
InputStream is;
ZipInputStream zis;
try {
String filename;
is = new FileInputStream(path + zipname);
zis = new ZipInputStream(new BufferedInputStream(is));
ZipEntry ze;
byte[] buffer = new byte[1024];
int count;
while ((ze = zis.getNextEntry()) != null) {
filename = ze.getName();
if (ze.isDirectory()) {
File fmd = new File(path + filename);
fmd.mkdirs();
continue;
}
//ADD THIS//
if (filename.contains("/")){
String[] folders = filename.split("/");
for (String item : folders)
{
File fmd = new File(path + item);
if (!item.contains(".") && !fmd.exists()){
fmd.mkdirs();
Log.d("created folder", item);
}
}
}
FileOutputStream fout = new FileOutputStream(path + filename);
while ((count = zis.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
fout.close();
zis.closeEntry();
}
zis.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
Where you open the file as in File fmd = new File(path + filename); and FileOutputStream fout = new FileOutputStream(path + filename); you can simply prepend the target directory as path + filename is simply the path to the file. I'm not sure what you are passing in as path in this case, I assume the path to the zip file, if you wish to extract elsewhere you need to pass in another variable with the target. Something like:
public static boolean unpackZip(String path, String zipname, String outputPath) {
InputStream is;
ZipInputStream zis;
try {
String filename;
is = new FileInputStream(path + zipname);
zis = new ZipInputStream(new BufferedInputStream(is));
ZipEntry ze;
byte[] buffer = new byte[1024];
int count;
while ((ze = zis.getNextEntry()) != null) {
filename = ze.getName();
if (ze.isDirectory()) {
File fmd = new File(outputPath + filename);
fmd.mkdirs();
continue;
}
FileOutputStream fout = new FileOutputStream(outputPath + filename);
while ((count = zis.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
fout.close();
zis.closeEntry();
}
zis.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
Specify a target directory as a parameter. Apart this, your code seems ok.
public static boolean unpackZip(String path, String zipname, String targetDirectory) {
InputStream is;
ZipInputStream zis;
try {
String filename;
is = new FileInputStream(path + zipname);
zis = new ZipInputStream(new BufferedInputStream(is));
ZipEntry ze;
byte[] buffer = new byte[1024];
int count;
while ((ze = zis.getNextEntry()) != null) {
filename = ze.getName();
if (ze.isDirectory()) {
File fmd = new File(targetDirectory + filename);
fmd.mkdirs();
continue;
}
FileOutputStream fout = new FileOutputStream(targetDirectory + filename);
while ((count = zis.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
fout.close();
zis.closeEntry();
}
zis.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
I have several zip files that each contains several files too which I want to extract using the ZipInputStream class. Among them are some images. When I try to extract these images using the BufferedOutputStream they are decompressed partially and the images are incomplete.
private void extractArchives() {
ZipInputStream zis;
File archiveDir = new File(
Environment.getExternalStorageDirectory().getAbsolutePath() +
"/archives/");
File[] files = archiveDir.listFiles();
for (int i = 0; i < files.length; ++i)
{
File file = files[i];
try
{
zis = new ZipInputStream(new FileInputStream(file));
ZipEntry ze;
while ((ze = zis.getNextEntry()) != null)
{
BufferedOutputStream bos;
byte[] buffer = new byte[102400];
int count;
while ((count = zis.read(buffer)) != -1)
{
String fileName = ze.getName();
if (fileName.endsWith(".jpg"))
{
path += File.separator + fileName;
bos = new BufferedOutputStream(new FileOutputStream(path));
bos.write(buffer, 0, count);
bos.close();
}
}
}
zis.close();
}
catch(FileNotFoundException e) { continue; }
//If the file is not a zip file or is a directory
catch (IOException e) { continue; }
}
}
Is there anything wrong with the code above? Does using BufferedOutputStream cause this problem? I appreciate any ideas. Thanks.
I modified the method according to what Erwin said and now it works:
private void extractArchives() {
File archiveDir = new File(
Environment.getExternalStorageDirectory().getAbsolutePath() +
"/archives/");
String archivePath = archiveDir.getAbsolutePath();
File[] files = archiveDir.listFiles();
for (int i = 0; i < files.length; ++i)
{
File file = files[i];
if(!file.isDirectory())
{
try {
ZipInputStream zis = new ZipInputStream(new FileInputStream(file));
ZipEntry entry = zis.getNextEntry();
while (entry != null)
{
if(entry.getName().endsWith(".jpg"))
{
String imagePath = themePath + File.separator + entry.getName();
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(imagePath));
byte[] buffer = new byte[4096];
int read = 0;
while ((read = zis.read(buffer)) != -1) bos.write(buffer, 0, read);
imagePath = "";
bos.close();
}
zis.closeEntry();
entry = zis.getNextEntry();
}
zis.close();
}
catch (FileNotFoundException e) {}
catch (IOException e) {}
}
}
}
Focusing on the loop that iterates over the zip entries, the problem is that you were reading at most 102400 bytes from a zip entry and then writing that to a new file. The next maximum 102400 bytes if the same image file was written to a new file. If the path name was the same as the old file, it would overwrite the old file. But since you were constantly appending to the local variable 'path', I'm not sure where the data ended up.
Pull the opening of the FileOutputStream out of the inner loop, and keep writing to the same OutputStream until you can read no more bytes from the current ZipEntry. Only then move on to the next ZipEntry and to the next OutputStream.
This is a standard pattern to read and copy between streams in Java so I can fix your code without having the definitions of the files, path and zis but it would help if you can post an actual compiling code example so that other people can benefit from this too.
Another improvement you need in production code, is to wrap bos.close() in a try/finally block so that the output file is also closed if there was an exception while reading from the input.
while ((ze = zis.getNextEntry()) != null) {
String fileName = ze.getName();
if (fileName.endsWith(".jpg")) {
String filepath = path + File.separator + fileName;
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filepath));
byte[] buffer = new byte[102400];
int count;
while ((count = zis.read(buffer)) != -1) {
bos.write(buffer, 0, count);
}
bos.close();
}
}
i'm facing a problem with unzipping files in Android. Here is the code snippet:
public void unzip() {
try {
FileInputStream fin = new FileInputStream(_zipFile);
BufferedInputStream in = new BufferedInputStream(fin);
ZipInputStream zin = new ZipInputStream(in);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
Log.v("Decompress", "Unzipping " + ze.getName());
if(ze.isDirectory()) {
_dirChecker(ze.getName());
} else {
FileOutputStream fout = new FileOutputStream(_location + ze.getName());
BufferedOutputStream out = new BufferedOutputStream(fout);
byte[] buffer = new byte[1024];
int length;
while ((length = zin.read(buffer,0,1024)) >= 0) {
out.write(buffer,0,length);
}
/* while ((length = zin.read(buffer))>0) {
out.write(buffer, 0, length);
}*/
/*for (int c = zin.read(); c != -1; c = zin.read()) {
fout.write(c);
}*/
zin.closeEntry();
fout.close();
}
}
zin.close();
} catch(Exception e) {
Log.e("Decompress", "unzip", e);
}
}
Smaller files (smaller than 10kB) are unzipped like empty - size 0 (html files, .jpg). Other files are ok. If I use this same code, but without buffers all the files are ok - ofcourse, unzipping without buffers is out of the question since it runs too long. Files are stored on SD card on real device. I have already tried setting smaller buffer size ( even new byte[2]). Thanks in advance...
Try this code instead,
public void doUnzip(String inputZipFile, String destinationDirectory)
throws IOException {
int BUFFER = 2048;
List zipFiles = new ArrayList();
File sourceZipFile = new File(inputZip);
File unzipDestinationDirectory = new File(destinationDirectory);
unzipDestinationDirectory.mkdir();
ZipFile zipFile;
// Open Zip file for reading
zipFile = new ZipFile(sourceZipFile, ZipFile.OPEN_READ);
// Create an enumeration of the entries in the zip file
Enumeration zipFileEntries = zipFile.entries();
// Process each entry
while (zipFileEntries.hasMoreElements()) {
// grab a zip file entry
ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
String currentEntry = entry.getName();
File destFile = new File(unzipDestinationDirectory, currentEntry);
// destFile = new File(unzipDestinationDirectory, destFile.getName());
if (currentEntry.endsWith(".zip")) {
zipFiles.add(destFile.getAbsolutePath());
}
// grab file's parent directory structure
File destinationParent = destFile.getParentFile();
// create the parent directory structure if needed
destinationParent.mkdirs();
try {
// extract file if not a directory
if (!entry.isDirectory()) {
BufferedInputStream is =
new BufferedInputStream(zipFile.getInputStream(entry));
int currentByte;
// establish buffer for writing file
byte data[] = new byte[BUFFER];
// write the current file to disk
FileOutputStream fos = new FileOutputStream(destFile);
BufferedOutputStream dest =
new BufferedOutputStream(fos, BUFFER);
// read and write until last byte is encountered
while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, currentByte);
}
dest.flush();
dest.close();
is.close();
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
zipFile.close();
for (Iterator iter = zipFiles.iterator(); iter.hasNext();) {
String zipName = (String)iter.next();
doUnzip(
zipName,
destinationDirectory +
File.separatorChar +
zipName.substring(0,zipName.lastIndexOf(".zip"))
);
}
}