|
|
@ -26,20 +26,26 @@ using System.IO; |
|
|
|
using System.IO.Compression; |
|
|
|
using System.Text; |
|
|
|
using iText.Commons.Exceptions; |
|
|
|
using iText.Commons.Logs; |
|
|
|
using Microsoft.Extensions.Logging; |
|
|
|
|
|
|
|
namespace iText.Commons.Utils { |
|
|
|
/// <summary>Allows reading entries from a zip file.</summary>
|
|
|
|
public class ZipFileReader : IDisposable { |
|
|
|
|
|
|
|
private readonly ZipArchive zipArchive; |
|
|
|
private static readonly ILogger LOGGER = ITextLogManager.GetLogger(typeof(ZipFileReader)); |
|
|
|
|
|
|
|
private readonly ZipArchive zipArchive; |
|
|
|
|
|
|
|
private int thresholdSize = 1_000_000_000; |
|
|
|
private int thresholdEntries = 10000; |
|
|
|
private double thresholdRatio = 10; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Creates an instance for zip file reading.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="archivePath">the path to the zip file to read</param>
|
|
|
|
/// <exception cref="IOException">if some I/O exception occurs</exception>
|
|
|
|
public ZipFileReader(String archivePath) |
|
|
|
{ |
|
|
|
public ZipFileReader(String archivePath) { |
|
|
|
if (archivePath == null) { |
|
|
|
throw new IOException(CommonsExceptionMessageConstant.FILE_NAME_CAN_NOT_BE_NULL); |
|
|
|
} |
|
|
@ -52,9 +58,44 @@ namespace iText.Commons.Utils { |
|
|
|
/// Get all file entries paths inside the reading zip file.
|
|
|
|
/// </summary>
|
|
|
|
/// <returns>the {@link Set} of all file entries paths</returns>
|
|
|
|
/// <exception cref="IOException">if some I/O exception occurs</exception>
|
|
|
|
public ISet<String> GetFileNames() { |
|
|
|
ISet<String> fileNames = new HashSet<String>(); |
|
|
|
|
|
|
|
int totalSizeArchive = 0; |
|
|
|
int totalEntryArchive = 0; |
|
|
|
foreach (ZipArchiveEntry entry in zipArchive.Entries) { |
|
|
|
Boolean zipBombSuspicious = false; |
|
|
|
totalEntryArchive++; |
|
|
|
using (Stream stream = entry.Open()) { |
|
|
|
int nBytes; |
|
|
|
byte[] buffer = new byte[2048]; |
|
|
|
int totalSizeEntry = 0; |
|
|
|
while ((nBytes = stream.Read(buffer, 0, 2048)) > 0) { |
|
|
|
totalSizeEntry += nBytes; |
|
|
|
totalSizeArchive += nBytes; |
|
|
|
double compressionRatio = (double) totalSizeEntry / entry.CompressedLength; |
|
|
|
if (compressionRatio > thresholdRatio) { |
|
|
|
zipBombSuspicious = true; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
if (zipBombSuspicious) { |
|
|
|
LOGGER.LogWarning(MessageFormatUtil.Format(CommonsLogMessageConstant.RATIO_IS_HIGHLY_SUSPICIOUS, |
|
|
|
thresholdRatio)); |
|
|
|
break; |
|
|
|
} |
|
|
|
if (totalSizeArchive > thresholdSize) { |
|
|
|
LOGGER.LogWarning(MessageFormatUtil.Format(CommonsLogMessageConstant.UNCOMPRESSED_DATA_SIZE_IS_TOO_MUCH, |
|
|
|
thresholdSize)); |
|
|
|
break; |
|
|
|
} |
|
|
|
if (totalEntryArchive > thresholdEntries) { |
|
|
|
LOGGER.LogWarning(MessageFormatUtil.Format(CommonsLogMessageConstant.TOO_MUCH_ENTRIES_IN_ARCHIVE, |
|
|
|
thresholdEntries)); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
String entryName = entry.FullName; |
|
|
|
if (!IsDirectory(entryName)) { |
|
|
|
fileNames.Add(entryName); |
|
|
@ -81,6 +122,32 @@ namespace iText.Commons.Utils { |
|
|
|
return entry.Open(); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Sets the maximum total uncompressed data size to prevent a Zip Bomb Attack.
|
|
|
|
/// Default value is 1 GB (1000000000).
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="thresholdSize"> the threshold for maximum total size of the uncompressed data</param>
|
|
|
|
public void SetThresholdSize(int thresholdSize) { |
|
|
|
this.thresholdSize = thresholdSize; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Sets the maximum number of file entries in the archive to prevent a Zip Bomb Attack. Default value is 10000.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="thresholdEntries"> maximum number of file entries in the archive</param>
|
|
|
|
public void SetThresholdEntries(int thresholdEntries) { |
|
|
|
this.thresholdEntries = thresholdEntries; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Sets the maximum ratio between compressed and uncompressed data to prevent a Zip Bomb Attack. In general
|
|
|
|
/// the data compression ratio for most of the legit archives is 1 to 3. Default value is 10.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="thresholdRatio"> maximum ratio between compressed and uncompressed data</param>
|
|
|
|
public void SetThresholdRatio(double thresholdRatio) { |
|
|
|
this.thresholdRatio = thresholdRatio; |
|
|
|
} |
|
|
|
|
|
|
|
public void Dispose() { |
|
|
|
zipArchive.Dispose(); |
|
|
|
} |
|
|
|