A Bug Encountered When Unzipping Zip Files in C# at the End of 2022

A Bug Encountered When Unzipping Zip Files in C# at the End of 2022

Recently, while troubleshooting an upload feature, the client uploaded a zip file. On the server side, the code using C# to unzip the file would decompress it and then verify whether it is an allowed file type. It also had to check for tampered file extensions, file headers, etc. But then, during the unzipping of the zip file, an unexpected problem occurred.

Last updated 12/23/2022 9:39 AM
江湖人士
4 min read
Category
.NET
Tags
.NET C#

This article is contributed by a netizen.

Author: Jianghu People

Original title: A Bug Encountered When Unzipping Zip Files in C# at the End of 2022

Original link: https://jhrs.com/2022/46060.html

Recently, while troubleshooting an upload feature, the client uploads a zip file. On the server side, C# code is used to unzip the uploaded file and verify whether it is an allowed file type. Additionally, checks for tampered file extensions, file headers, etc., are performed. However, an error occurred during the unzipping process.

Unzipping Zip Files in C#

First, let me provide some context (or background). A file service site was deployed on IIS for uploading various files. The process involves first uploading to a randomly created temporary directory under the site root (a lazy approach: directly using a GUID as the directory name). After file verification, the file is then cut or copied to the formal archive directory. For code on copying or moving files in C#, you can refer to this article from Jianghu People. Yesterday, just before leaving work, an error occurred when uploading a zip file. Many GUID-named directories were created under the file service root site. Oh my, this was a disaster. Something abnormal must have happened, indicating a code bug.

The Buggy Unzip Code

It’s almost the end of 2022, and this bug popped up. I quickly set up a simulation environment to test it and found that the original code indeed had issues. The original code is as follows:

/// <summary>
/// Unzip files
/// </summary>
/// <param name="saveDir">Save directory</param>
/// <param name="stream"></param>
public static void UnZipFiles(string saveDir, Stream stream)
{
    using (ZipInputStream s = new ZipInputStream(stream))
    {
        ZipEntry theEntry;
        while ((theEntry = s.GetNextEntry()) != null)
        {
            string directoryName = $"{saveDir}{Path.GetDirectoryName(theEntry.Name)}\\";
            string fileName = Path.GetFileName(theEntry.Name);
            Directory.CreateDirectory(directoryName);
            using (FileStream streamWriter = File.Create(directoryName + fileName))
            {
                byte[] data = new byte[2048];
                while (true)
                {
                    int size = s.Read(data, 0, data.Length);
                    if (size > 0)
                    {
                        streamWriter.Write(data, 0, size);
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }
    }
}

By the way, it’s worth noting that this is an old project, so the compression and decompression used the ICSharpCode.SharpZipLib.Zip component.

When opening the source code, the problem was immediately obvious: the logic was not rigorous, as the save directory for decompressed files was directly concatenated.

How to Fix This Bug?

Once the issue was identified, fixing it was straightforward. Simply call the Path.Combine method. Also, during decompression, check whether the entry is a directory or a file. The final fixed code is as follows:

/// <summary>
/// Unzip files
/// </summary>
/// <param name="saveDir">Save directory</param>
/// <param name="stream"></param>
public static void UnZipFiles(string saveDir, Stream stream)
{
    using (ZipInputStream s = new ZipInputStream(stream))
    {
        ZipEntry theEntry;
        string directoryName, file, fileName;
        while ((theEntry = s.GetNextEntry()) != null)
        {
            directoryName = Path.Combine(saveDir, Path.GetDirectoryName(theEntry.Name));
            fileName = Path.GetFileName(theEntry.Name);
            Directory.CreateDirectory(directoryName);
            file = Path.Combine(directoryName, fileName);

            if (theEntry.IsFile)
            {
                using (FileStream streamWriter = File.Create(file))
                {
                    byte[] data = new byte[2048];
                    while (true)
                    {
                        int size = s.Read(data, 0, data.Length);
                        if (size > 0)
                        {
                            streamWriter.Write(data, 0, size);
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
        }
    }
}
Keep Exploring

Related Reading

More Articles