Files
codeql/csharp/ql/test/query-tests/Security Features/CWE-022/ZipSlip/ZipSlip.cs
Luke Cartey 1a90f7df2c C#: ZipSlip - Address review comments.
- Add backticks
 - Add extra test.
2018-10-03 11:38:48 +01:00

141 lines
5.8 KiB
C#

// semmle-extractor-options: /r:System.IO.Compression.dll /r:System.IO.Compression.FileSystem.dll /r:System.IO.Compression.ZipFile.dll /r:System.IO.FileSystem.dll
using System;
using System.IO;
using System.IO.Compression;
namespace ZipSlip
{
class Program
{
public static void UnzipFileByFile(ZipArchive archive,
string destDirectory)
{
foreach (var entry in archive.Entries)
{
string fullPath = Path.GetFullPath(entry.FullName);
string fileName = Path.GetFileName(entry.FullName);
string filename = entry.Name;
string file = entry.FullName;
if (!string.IsNullOrEmpty(file))
{
// BAD
string destFileName = Path.Combine(destDirectory, file);
entry.ExtractToFile(destFileName, true);
// GOOD
string sanitizedFileName = Path.Combine(destDirectory, fileName);
entry.ExtractToFile(sanitizedFileName, true);
// BAD
string destFilePath = Path.Combine(destDirectory, fullPath);
entry.ExtractToFile(destFilePath, true);
// BAD: destFilePath isn't fully resolved, so may still contain ..
if (destFilePath.StartsWith(destDirectory))
entry.ExtractToFile(destFilePath, true);
// BAD
destFilePath = Path.GetFullPath(Path.Combine(destDirectory, fullPath));
entry.ExtractToFile(destFilePath, true);
// GOOD: a check for StartsWith against a fully resolved path
if (destFilePath.StartsWith(destDirectory))
entry.ExtractToFile(destFilePath, true);
}
}
}
private static int UnzipToStream(Stream zipStream, string installDir)
{
int returnCode = 0;
try
{
// normalize InstallDir for use in check below
var InstallDir = Path.GetFullPath(installDir + Path.DirectorySeparatorChar);
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Read))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
// figure out where we are putting the file
String destFilePath = Path.Combine(InstallDir, entry.FullName);
Directory.CreateDirectory(Path.GetDirectoryName(destFilePath));
using (Stream archiveFileStream = entry.Open())
{
// BAD: writing to file stream
using (Stream tfsFileStream = new FileStream(destFilePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None))
{
Console.WriteLine(@"Writing ""{0}""", destFilePath);
archiveFileStream.CopyTo(tfsFileStream);
}
// BAD: can do it this way too
using (Stream tfsFileStream = File.Create(destFilePath))
{
Console.WriteLine(@"Writing ""{0}""", destFilePath);
archiveFileStream.CopyTo(tfsFileStream);
}
// BAD: creating stream using fileInfo
var fileInfo = new FileInfo(destFilePath);
using (FileStream fs = fileInfo.OpenWrite())
{
Console.WriteLine(@"Writing ""{0}""", destFilePath);
archiveFileStream.CopyTo(fs);
}
// BAD: creating stream using fileInfo
var fileInfo1 = new FileInfo(destFilePath);
using (FileStream fs = fileInfo1.Open(FileMode.Create))
{
Console.WriteLine(@"Writing ""{0}""", destFilePath);
archiveFileStream.CopyTo(fs);
}
// GOOD: Use substring to pick out single component
string fileName = destFilePath.Substring(destFilePath.LastIndexOf("\\"));
var fileInfo2 = new FileInfo(fileName);
using (FileStream fs = fileInfo2.Open(FileMode.Create))
{
Console.WriteLine(@"Writing ""{0}""", destFilePath);
archiveFileStream.CopyTo(fs);
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Error patching files: {0}", ex.ToString());
returnCode = -1;
}
return returnCode;
}
static void Main(string[] args)
{
string zipFileName;
zipFileName = args[0];
string targetPath = args.Length == 2 ? args[1] : ".";
using (FileStream file = new FileStream(zipFileName, FileMode.Open))
{
using (ZipArchive archive = new ZipArchive(file, ZipArchiveMode.Read))
{
UnzipFileByFile(archive, targetPath);
// GOOD: the path is checked in this extension method
archive.ExtractToDirectory(targetPath);
UnzipToStream(file, targetPath);
}
}
}
}
}