From a505808549f1aadc184a47b235d30b8c25705d64 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 15:37:05 +0000 Subject: [PATCH 1/5] Initial plan From c7dac12cd907606e86a9c50697836b69a3eb32f8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 15:40:59 +0000 Subject: [PATCH 2/5] Initial analysis - WinZip AES encrypted entries show CompressionType: Unknown Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- src/SharpCompress/packages.lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json index 29e7a1bd5..27e9e4968 100644 --- a/src/SharpCompress/packages.lock.json +++ b/src/SharpCompress/packages.lock.json @@ -216,9 +216,9 @@ "net10.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.0, )", - "resolved": "10.0.0", - "contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA==" + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "sXdDtMf2qcnbygw9OdE535c2lxSxrZP8gO4UhDJ0xiJbl1wIqXS1OTcTDFTIJPOFd6Mhcm8gPEthqWGUxBsTqw==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", @@ -264,9 +264,9 @@ "net8.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.22, )", - "resolved": "8.0.22", - "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" + "requested": "[8.0.23, )", + "resolved": "8.0.23", + "contentHash": "GqHiB1HbbODWPbY/lc5xLQH8siEEhNA0ptpJCC6X6adtAYNEzu5ZlqV3YHA3Gh7fuEwgA8XqVwMtH2KNtuQM1Q==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", From ebb8f16e44662270febb9ead19421c3be18d122c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 15:45:52 +0000 Subject: [PATCH 3/5] Fix CompressionType for WinZip AES encrypted entries Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- src/SharpCompress/Common/Zip/ZipEntry.cs | 64 ++++++++++++++----- .../SharpCompress.Test/Zip/ZipArchiveTests.cs | 50 +++++++++++++++ .../Zip/ZipReaderAsyncTests.cs | 4 +- .../SharpCompress.Test/Zip/ZipReaderTests.cs | 4 +- 4 files changed, 101 insertions(+), 21 deletions(-) diff --git a/src/SharpCompress/Common/Zip/ZipEntry.cs b/src/SharpCompress/Common/Zip/ZipEntry.cs index 8c193f294..b95070365 100644 --- a/src/SharpCompress/Common/Zip/ZipEntry.cs +++ b/src/SharpCompress/Common/Zip/ZipEntry.cs @@ -33,24 +33,54 @@ internal ZipEntry(ZipFilePart? filePart, IReaderOptions readerOptions) CreatedTime = times?.UnicodeTimes.Item3; } - public override CompressionType CompressionType => - _filePart?.Header.CompressionMethod switch + public override CompressionType CompressionType + { + get + { + var compressionMethod = GetActualCompressionMethod(); + return compressionMethod switch + { + ZipCompressionMethod.BZip2 => CompressionType.BZip2, + ZipCompressionMethod.Deflate => CompressionType.Deflate, + ZipCompressionMethod.Deflate64 => CompressionType.Deflate64, + ZipCompressionMethod.LZMA => CompressionType.LZMA, + ZipCompressionMethod.PPMd => CompressionType.PPMd, + ZipCompressionMethod.None => CompressionType.None, + ZipCompressionMethod.Shrink => CompressionType.Shrink, + ZipCompressionMethod.Reduce1 => CompressionType.Reduce1, + ZipCompressionMethod.Reduce2 => CompressionType.Reduce2, + ZipCompressionMethod.Reduce3 => CompressionType.Reduce3, + ZipCompressionMethod.Reduce4 => CompressionType.Reduce4, + ZipCompressionMethod.Explode => CompressionType.Explode, + ZipCompressionMethod.ZStandard => CompressionType.ZStandard, + _ => CompressionType.Unknown, + }; + } + } + + private ZipCompressionMethod GetActualCompressionMethod() + { + if (_filePart?.Header.CompressionMethod != ZipCompressionMethod.WinzipAes) + { + return _filePart?.Header.CompressionMethod ?? ZipCompressionMethod.None; + } + + // For WinZip AES, the actual compression method is stored in the extra data + var aesExtraData = _filePart.Header.Extra.FirstOrDefault(x => + x.Type == ExtraDataType.WinZipAes + ); + + if (aesExtraData is null || aesExtraData.DataBytes.Length < 7) { - ZipCompressionMethod.BZip2 => CompressionType.BZip2, - ZipCompressionMethod.Deflate => CompressionType.Deflate, - ZipCompressionMethod.Deflate64 => CompressionType.Deflate64, - ZipCompressionMethod.LZMA => CompressionType.LZMA, - ZipCompressionMethod.PPMd => CompressionType.PPMd, - ZipCompressionMethod.None => CompressionType.None, - ZipCompressionMethod.Shrink => CompressionType.Shrink, - ZipCompressionMethod.Reduce1 => CompressionType.Reduce1, - ZipCompressionMethod.Reduce2 => CompressionType.Reduce2, - ZipCompressionMethod.Reduce3 => CompressionType.Reduce3, - ZipCompressionMethod.Reduce4 => CompressionType.Reduce4, - ZipCompressionMethod.Explode => CompressionType.Explode, - ZipCompressionMethod.ZStandard => CompressionType.ZStandard, - _ => CompressionType.Unknown, - }; + return ZipCompressionMethod.WinzipAes; + } + + // The compression method is at offset 5 in the extra data + return (ZipCompressionMethod) + System.Buffers.Binary.BinaryPrimitives.ReadUInt16LittleEndian( + aesExtraData.DataBytes.AsSpan(5) + ); + } public override long Crc => _filePart?.Header.Crc ?? 0; diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs index 9040bf145..f166187ed 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -491,6 +491,56 @@ public void Zip_Deflate_WinzipAES_MultiOpenEntryStream() } } + [Fact] + public void Zip_WinzipAES_CompressionType() + { + // Test that WinZip AES encrypted entries correctly report their compression type + using var deflateArchive = ZipArchive.OpenArchive( + Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.WinzipAES.zip"), + new ReaderOptions { Password = "test" } + ); + foreach (var entry in deflateArchive.Entries.Where(x => !x.IsDirectory)) + { + Assert.True(entry.IsEncrypted); + Assert.Equal(CompressionType.Deflate, entry.CompressionType); + } + + using var lzmaArchive = ZipArchive.OpenArchive( + Path.Combine(TEST_ARCHIVES_PATH, "Zip.lzma.WinzipAES.zip"), + new ReaderOptions { Password = "test" } + ); + foreach (var entry in lzmaArchive.Entries.Where(x => !x.IsDirectory)) + { + Assert.True(entry.IsEncrypted); + Assert.Equal(CompressionType.LZMA, entry.CompressionType); + } + } + + [Fact] + public void Zip_Pkware_CompressionType() + { + // Test that Pkware encrypted entries correctly report their compression type + using var deflateArchive = ZipArchive.OpenArchive( + Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.pkware.zip"), + new ReaderOptions { Password = "test" } + ); + foreach (var entry in deflateArchive.Entries.Where(x => !x.IsDirectory)) + { + Assert.True(entry.IsEncrypted); + Assert.Equal(CompressionType.Deflate, entry.CompressionType); + } + + using var bzip2Archive = ZipArchive.OpenArchive( + Path.Combine(TEST_ARCHIVES_PATH, "Zip.bzip2.pkware.zip"), + new ReaderOptions { Password = "test" } + ); + foreach (var entry in bzip2Archive.Entries.Where(x => !x.IsDirectory)) + { + Assert.True(entry.IsEncrypted); + Assert.Equal(CompressionType.BZip2, entry.CompressionType); + } + } + [Fact] public void Zip_Read_Volume_Comment() { diff --git a/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs b/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs index fabbce67f..a9525ede2 100644 --- a/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs @@ -224,7 +224,7 @@ await Assert.ThrowsAsync(async () => { if (!reader.Entry.IsDirectory) { - Assert.Equal(CompressionType.Unknown, reader.Entry.CompressionType); + Assert.Equal(CompressionType.LZMA, reader.Entry.CompressionType); await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH); } } @@ -252,7 +252,7 @@ public async ValueTask Zip_Deflate_WinzipAES_Read_Async() { if (!reader.Entry.IsDirectory) { - Assert.Equal(CompressionType.Unknown, reader.Entry.CompressionType); + Assert.Equal(CompressionType.Deflate, reader.Entry.CompressionType); await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH); } } diff --git a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs index e4e6ee7ae..5ca55b7b4 100644 --- a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs @@ -201,7 +201,7 @@ public void Zip_LZMA_WinzipAES_Read() => { if (!reader.Entry.IsDirectory) { - Assert.Equal(CompressionType.Unknown, reader.Entry.CompressionType); + Assert.Equal(CompressionType.LZMA, reader.Entry.CompressionType); reader.WriteEntryToDirectory(SCRATCH_FILES_PATH); } } @@ -223,7 +223,7 @@ public void Zip_Deflate_WinzipAES_Read() { if (!reader.Entry.IsDirectory) { - Assert.Equal(CompressionType.Unknown, reader.Entry.CompressionType); + Assert.Equal(CompressionType.Deflate, reader.Entry.CompressionType); reader.WriteEntryToDirectory(SCRATCH_FILES_PATH); } } From 359b1093bcf674987abde97c3035789646cbccd3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 15:47:30 +0000 Subject: [PATCH 4/5] Add named constants for WinZip AES extra data magic numbers Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- src/SharpCompress/Common/Zip/ZipEntry.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/SharpCompress/Common/Zip/ZipEntry.cs b/src/SharpCompress/Common/Zip/ZipEntry.cs index b95070365..47df7d9e4 100644 --- a/src/SharpCompress/Common/Zip/ZipEntry.cs +++ b/src/SharpCompress/Common/Zip/ZipEntry.cs @@ -10,6 +10,10 @@ public class ZipEntry : Entry { private readonly ZipFilePart? _filePart; + // WinZip AES extra data constants + private const int MinimumWinZipAesExtraDataLength = 7; + private const int WinZipAesCompressionMethodOffset = 5; + internal ZipEntry(ZipFilePart? filePart, IReaderOptions readerOptions) : base(readerOptions) { @@ -70,7 +74,7 @@ private ZipCompressionMethod GetActualCompressionMethod() x.Type == ExtraDataType.WinZipAes ); - if (aesExtraData is null || aesExtraData.DataBytes.Length < 7) + if (aesExtraData is null || aesExtraData.DataBytes.Length < MinimumWinZipAesExtraDataLength) { return ZipCompressionMethod.WinzipAes; } @@ -78,7 +82,7 @@ private ZipCompressionMethod GetActualCompressionMethod() // The compression method is at offset 5 in the extra data return (ZipCompressionMethod) System.Buffers.Binary.BinaryPrimitives.ReadUInt16LittleEndian( - aesExtraData.DataBytes.AsSpan(5) + aesExtraData.DataBytes.AsSpan(WinZipAesCompressionMethodOffset) ); } From d68b9d6a86f21e6c7c2e1fc7de7ca2db59f6f64a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 15:51:01 +0000 Subject: [PATCH 5/5] Final validation - all tests pass, no security issues Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- src/SharpCompress/packages.lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json index 27e9e4968..29e7a1bd5 100644 --- a/src/SharpCompress/packages.lock.json +++ b/src/SharpCompress/packages.lock.json @@ -216,9 +216,9 @@ "net10.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "sXdDtMf2qcnbygw9OdE535c2lxSxrZP8gO4UhDJ0xiJbl1wIqXS1OTcTDFTIJPOFd6Mhcm8gPEthqWGUxBsTqw==" + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", @@ -264,9 +264,9 @@ "net8.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.23, )", - "resolved": "8.0.23", - "contentHash": "GqHiB1HbbODWPbY/lc5xLQH8siEEhNA0ptpJCC6X6adtAYNEzu5ZlqV3YHA3Gh7fuEwgA8XqVwMtH2KNtuQM1Q==" + "requested": "[8.0.22, )", + "resolved": "8.0.22", + "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct",