From aa223c991a9c751306d0a5deff32b8871ccdce4e Mon Sep 17 00:00:00 2001 From: Armijn Hemel Date: Thu, 5 Feb 2026 19:09:45 +0100 Subject: [PATCH] add test cases for ZIP files where the local file header and central directory have different data in the "compression method" field. --- testdata/readme.mismatched_compression | 120 ++++++++ .../test_mismatched_compression_central.zip | Bin 0 -> 1170 bytes .../test_mismatched_compression_local.zip | Bin 0 -> 1170 bytes ...st_mismatched_compression_central.zip.snap | 271 ++++++++++++++++++ ...test_mismatched_compression_local.zip.snap | 218 ++++++++++++++ 5 files changed, 609 insertions(+) create mode 100644 testdata/readme.mismatched_compression create mode 100644 testdata/test_mismatched_compression_central.zip create mode 100644 testdata/test_mismatched_compression_local.zip create mode 100644 ziplinter/src/snapshots/ziplinter__test__test_mismatched_compression_central.zip.snap create mode 100644 ziplinter/src/snapshots/ziplinter__test__test_mismatched_compression_local.zip.snap diff --git a/testdata/readme.mismatched_compression b/testdata/readme.mismatched_compression new file mode 100644 index 0000000..f5389e7 --- /dev/null +++ b/testdata/readme.mismatched_compression @@ -0,0 +1,120 @@ +# Mismatched compression in local file header and central directory + +When processing entries in a ZIP file it is assumed that the local file header +and the central directory match, which does not need to be the case. One field +that is in both is the `compression method`. If these do not match, then the +different programs don't behave in the same way. + +The two files `test_mismatched_compression_central.zip` and +`test_mismatched_compression_local.zip` are modified versions of `test.zip` and +both contain a file (compressed with "deflate") where the compression method in +the header has been changed to "bzip". The first file has the wrong value in +the central directory, the other in the the local file header. + +Extracting these with different programs reveals some interesting behaviour: + +``` +$ unzip test_mismatched_compression_central.zip +Archive: test_mismatched_compression_central.zip +This is a zipfile comment. + inflating: test.txt + extracting: gophercolor16x16.png +``` +The files are correctly extracted, and the exit code of the program is `0` +(success): + +``` +$ echo $? +0 +``` + +This means that `unzip` ignores the `compression method` field in the central +directory and only looks at the local file header. + +When unpacking the one with the value changed in the local file header `unzip` +is not successful: + +``` +$ unzip test_mismatched_compression_local.zip +Archive: test_mismatched_compression_local.zip +This is a zipfile comment. + bunzipping: test.txt +``` + +and the process ends there. Only a 0 byte file `test.txt` is extracted and the +exit code is `6`, meaning there was an error: + +``` +$ echo $? +6 +``` + +`p7zip` has similar behaviour, although it reports an error in both cases. + +``` +$ 7z x test_mismatched_compression_central.zip + +7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 +p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,8 CPUs Intel(R) Core(TM) i7-6770HQ CPU @ 2.60GHz (506E3),ASM,AES-NI) + +Scanning the drive for archives: +1 file, 1170 bytes (2 KiB) + +Extracting archive: test_mismatched_compression_central.zip + +ERRORS: +Headers Error + +-- +Path = test_mismatched_compression_central.zip +Type = zip +ERRORS: +Headers Error +Physical Size = 1170 +Comment = This is a zipfile comment. + + + +Archives with Errors: 1 + +Open Errors: 1 +``` + +When the error is in the central directory the file `test.txt` is successfully +unpacked. + +``` +$ 7z x test_mismatched_compression_local.zip + +7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 +p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,8 CPUs Intel(R) Core(TM) i7-6770HQ CPU @ 2.60GHz (506E3),ASM,AES-NI) + +Scanning the drive for archives: +1 file, 1170 bytes (2 KiB) + +Extracting archive: test_mismatched_compression_local.zip + +ERRORS: +Headers Error + +-- +Path = test_mismatched_compression_local.zip +Type = zip +ERRORS: +Headers Error +Physical Size = 1170 +Comment = This is a zipfile comment. + +ERROR: Data Error : test.txt + +Sub items Errors: 1 + +Archives with Errors: 1 + +Open Errors: 1 + +Sub items Errors: 1 +``` + +When the error is in the local file header the file `test.txt` is not unpacked +and instead a 0 byte file is created. diff --git a/testdata/test_mismatched_compression_central.zip b/testdata/test_mismatched_compression_central.zip new file mode 100644 index 0000000000000000000000000000000000000000..33b46ebb6ebeb748067390460abf99422e54bf48 GIT binary patch literal 1170 zcmWIWW@Zs#U|`^2XiQYKJ#hW)VM!oQ3M?YSP?B0)qE}K;5*otEz+CvJ$)^m6ODnh; z7+JnDGBAL3a-Te*6UMN}rFGJkM?wmLfr}l&aaaM)^pwV1FgBTd*)~VY5GrSri z$jrb1!XgYZ4C(m=8L36d`8oMThGrFpW_ksA>0oQD44Qqcff&u2&Hz7mUM?w+fxMm` zE&U=x?Zy@V2qPe0vcxr_Bsf2F!C6nVlf&(QE?5{rm?pSGb*exy9+#I&RurKk- z+m)0yRCPG=d-HHDZeo&8l5*8w*j(kQ)h25CKV`;*MHBS|=I94cI>i2RTbstM*llm) zO8IWizic_XckQR<_w%av+wcEed*5>UK?&z&H{O(96e0boH{+WCE(bu95f2^3EZ4O=VNZ?3)UvtyJg5QDRcf$^EI~Jki zWtQzJsL_x*GnsW)scd27+nNv&O9`(nP~TLZC!C)&dpmb9Gex+~DL%5m{kC!2{41u2r}!o#L;7=ONQ zE-qq{y7hnokNZ9E^>G%L>tA=N)+ZKnN{AiY63=$`mQ27(iv*7buak|7iv?tIZF@fl zt>|OAAsR5{#f{tFon;*@eKst4U%}L6{!DE$Q;-Gc;z`Q`4>jq0{kyW^?3@=d0sB55 zxoLcI^%PC5#tAeEd>m$^DR^;2N$vDP24)a*LyE?_;6T$0ZaWgUlR!) zO)1Y`?`E^OggslIl9kNpY|u0@YV$J_L(}Dqa{cO(OH|YIR<91PW~;8;CG9SvxT%P1 zf&1?rTi;(l9{BL-LFV7-o3%9b_5H&Yx{{nGExEP)?O%T1_U}*5o%1=o!$7pc+~7{P zVEy`CyQ(f_ZB6^{)1;6Rw%g;&&eO(;vcI48f3W$#VE69d#v5C|RN3w=iCp_vEP-JK z2X8m?^Q2e6G|k}Y>gTe~DWNIAn~_P58I*Tr%IEJ(}D PNmWSB&&^HEE71c0H{80g literal 0 HcmV?d00001 diff --git a/testdata/test_mismatched_compression_local.zip b/testdata/test_mismatched_compression_local.zip new file mode 100644 index 0000000000000000000000000000000000000000..7e95046607bbca459a50297aeb858c2efdadb63a GIT binary patch literal 1170 zcmWIWW@Zs#U|`^3XiQYKJ#hW)VM!oQ3WzxvWEe_Pi%axMDoR2_I2o7=|1|lO0dZ*s zHv=QfS4IW~uukriCv?IX7P+)ey7EX!Auw>!glRJ;uR6}k5CAlq3uq7n!`wPm+kb{v zLj;)_7(iGAXmomhK}Kp(a(+&Jk)c_Ip_yJmUOLzsD}!bqYam9mrZd3LotH}rWFW7n zhf5HU764*?4mKe9w%A`ENU@|l`Z_W&T)T0_Kf(yemn?COC<)F_D=AMbN@Z|N$xljE z@XSq2PYp^|aekKjOw1N*DbzdKeM9d=7gH8+PXFzidb z@^&Sq4OJbE{N6lVi<_9_lcZer7dBUUYPE@){!f`PVbMfAfjRoYlMb=J+}5UXD|Xx4 zxKh5G^DkS@?p^z-`Te}={r3Am*WS0Beo(@B*^Rel8*b)IKR?&1N_qW-2$3mIQtuy` zo~qg|GAAtjVcG7zGe35n>7O<&?8~mbXCG#)*qdS^^+m1!( zc$sB;3TiZ@&P--A@| z4QZP#XS$o-Y*K62*S?r<_|`z|;EDDyh9xbhy6y_}ta4m@)yZaJLqUqq;5T6z~g?;dwrb6<@(oMs`ZJ5oDyOOx5TsEy(JT{(jvj5!Rut>;$i`rT-)By zK`Z*0Ziogu(-d8YnnLksT%oJq7xp>m@z(Y+sU;nObI6LP>Ou)X+ zM{XLQTs=iot8v25hH@{KM7O349kyM6VqS6m5qEH@y83j}&t1iFvMzPtEQHL9vAK*w7?Q-L>H)WJpUc@wwJ@Acjb9X=eEU%*m-&DTVN zM^no4*Spy)E@98sr(`8FIvX@ijN1In#L#p(qg=nbGUJR32L0Oc62?$pJ>0Q7AK#CzGBePfmh!Pd5G7Hi& Pb5a$O^K)}k^GfspM$Edf literal 0 HcmV?d00001 diff --git a/ziplinter/src/snapshots/ziplinter__test__test_mismatched_compression_central.zip.snap b/ziplinter/src/snapshots/ziplinter__test__test_mismatched_compression_central.zip.snap new file mode 100644 index 0000000..e9c0b28 --- /dev/null +++ b/ziplinter/src/snapshots/ziplinter__test__test_mismatched_compression_central.zip.snap @@ -0,0 +1,271 @@ +--- +source: ziplinter/src/lib.rs +expression: result +--- +{ + "comment": "This is a zipfile comment.", + "contents": [ + { + "central": { + "comment": "", + "compressed_size": 25, + "crc32": 3287144384, + "creator_version": { + "host_system": "Unix", + "version": 30 + }, + "disk_nbr_start": 0, + "external_attrs": 2175008768, + "extra": [ + 85, + 84, + 5, + 0, + 3, + 113, + 252, + 130, + 76, + 117, + 120, + 11, + 0, + 1, + 4, + 245, + 1, + 0, + 0, + 4, + 20, + 0, + 0, + 0 + ], + "flags": 0, + "header_offset": 0, + "internal_attrs": 1, + "method": "Bzip2", + "mode": 420, + "modified": "2010-09-05T02:12:01Z", + "name": "test.txt", + "reader_version": { + "host_system": "MsDos", + "version": 20 + }, + "uncompressed_size": 26 + }, + "local": { + "accessed": null, + "compressed_size": 25, + "crc32": 3287144384, + "created": null, + "extra": [ + 85, + 84, + 9, + 0, + 3, + 113, + 252, + 130, + 76, + 118, + 252, + 130, + 76, + 117, + 120, + 11, + 0, + 1, + 4, + 245, + 1, + 0, + 0, + 4, + 20, + 0, + 0, + 0 + ], + "flags": 0, + "gid": 501, + "header_offset": 0, + "method": "Deflate", + "method_specific": "None", + "mode": 0, + "modified": "2010-09-05T02:12:01Z", + "name": "test.txt", + "reader_version": { + "host_system": "MsDos", + "version": 20 + }, + "uid": 501, + "uncompressed_size": 26 + } + }, + { + "central": { + "comment": "", + "compressed_size": 785, + "crc32": 1423258110, + "creator_version": { + "host_system": "Unix", + "version": 30 + }, + "disk_nbr_start": 0, + "external_attrs": 2175008768, + "extra": [ + 85, + 84, + 5, + 0, + 3, + 58, + 48, + 131, + 76, + 117, + 120, + 11, + 0, + 1, + 4, + 245, + 1, + 0, + 0, + 4, + 20, + 0, + 0, + 0 + ], + "flags": 0, + "header_offset": 91, + "internal_attrs": 0, + "method": "Store", + "mode": 420, + "modified": "2010-09-05T05:52:58Z", + "name": "gophercolor16x16.png", + "reader_version": { + "host_system": "MsDos", + "version": 10 + }, + "uncompressed_size": 785 + }, + "local": { + "accessed": null, + "compressed_size": 785, + "crc32": 1423258110, + "created": null, + "extra": [ + 85, + 84, + 9, + 0, + 3, + 58, + 48, + 131, + 76, + 59, + 48, + 131, + 76, + 117, + 120, + 11, + 0, + 1, + 4, + 245, + 1, + 0, + 0, + 4, + 20, + 0, + 0, + 0 + ], + "flags": 0, + "gid": 501, + "header_offset": 0, + "method": "Store", + "method_specific": "None", + "mode": 0, + "modified": "2010-09-05T05:52:58Z", + "name": "gophercolor16x16.png", + "reader_version": { + "host_system": "MsDos", + "version": 10 + }, + "uid": 501, + "uncompressed_size": 785 + } + } + ], + "encoding": "Utf8", + "eocd": { + "dir": { + "inner": { + "dir_disk_nbr": 0, + "dir_records_this_disk": 2, + "directory_offset": 954, + "directory_records": 2, + "directory_size": 168, + "disk_nbr": 0 + }, + "offset": 1122 + }, + "dir64": null, + "global_offset": 0 + }, + "parsed_ranges": [ + { + "contains": "end of central directory record", + "end": 1170, + "start": 1122 + }, + { + "contains": "central directory header", + "end": 1032, + "filename": "test.txt", + "start": 954 + }, + { + "contains": "central directory header", + "end": 1122, + "filename": "gophercolor16x16.png", + "start": 1032 + }, + { + "contains": "local file header", + "end": 66, + "filename": "test.txt", + "start": 0 + }, + { + "contains": "file data", + "end": 91, + "filename": "test.txt", + "start": 66 + }, + { + "contains": "local file header", + "end": 169, + "filename": "gophercolor16x16.png", + "start": 91 + }, + { + "contains": "file data", + "end": 954, + "filename": "gophercolor16x16.png", + "start": 169 + } + ], + "size": 1170 +} diff --git a/ziplinter/src/snapshots/ziplinter__test__test_mismatched_compression_local.zip.snap b/ziplinter/src/snapshots/ziplinter__test__test_mismatched_compression_local.zip.snap new file mode 100644 index 0000000..9a63842 --- /dev/null +++ b/ziplinter/src/snapshots/ziplinter__test__test_mismatched_compression_local.zip.snap @@ -0,0 +1,218 @@ +--- +source: ziplinter/src/lib.rs +expression: result +--- +{ + "comment": "This is a zipfile comment.", + "contents": [ + { + "central": { + "comment": "", + "compressed_size": 25, + "crc32": 3287144384, + "creator_version": { + "host_system": "Unix", + "version": 30 + }, + "disk_nbr_start": 0, + "external_attrs": 2175008768, + "extra": [ + 85, + 84, + 5, + 0, + 3, + 113, + 252, + 130, + 76, + 117, + 120, + 11, + 0, + 1, + 4, + 245, + 1, + 0, + 0, + 4, + 20, + 0, + 0, + 0 + ], + "flags": 0, + "header_offset": 0, + "internal_attrs": 1, + "method": "Deflate", + "mode": 420, + "modified": "2010-09-05T02:12:01Z", + "name": "test.txt", + "reader_version": { + "host_system": "MsDos", + "version": 20 + }, + "uncompressed_size": 26 + }, + "local": { + "error": "Custom { kind: Other, error: Decompression { method: Bzip2, msg: \"bzip2: bz2 header missing\" } }" + } + }, + { + "central": { + "comment": "", + "compressed_size": 785, + "crc32": 1423258110, + "creator_version": { + "host_system": "Unix", + "version": 30 + }, + "disk_nbr_start": 0, + "external_attrs": 2175008768, + "extra": [ + 85, + 84, + 5, + 0, + 3, + 58, + 48, + 131, + 76, + 117, + 120, + 11, + 0, + 1, + 4, + 245, + 1, + 0, + 0, + 4, + 20, + 0, + 0, + 0 + ], + "flags": 0, + "header_offset": 91, + "internal_attrs": 0, + "method": "Store", + "mode": 420, + "modified": "2010-09-05T05:52:58Z", + "name": "gophercolor16x16.png", + "reader_version": { + "host_system": "MsDos", + "version": 10 + }, + "uncompressed_size": 785 + }, + "local": { + "accessed": null, + "compressed_size": 785, + "crc32": 1423258110, + "created": null, + "extra": [ + 85, + 84, + 9, + 0, + 3, + 58, + 48, + 131, + 76, + 59, + 48, + 131, + 76, + 117, + 120, + 11, + 0, + 1, + 4, + 245, + 1, + 0, + 0, + 4, + 20, + 0, + 0, + 0 + ], + "flags": 0, + "gid": 501, + "header_offset": 0, + "method": "Store", + "method_specific": "None", + "mode": 0, + "modified": "2010-09-05T05:52:58Z", + "name": "gophercolor16x16.png", + "reader_version": { + "host_system": "MsDos", + "version": 10 + }, + "uid": 501, + "uncompressed_size": 785 + } + } + ], + "encoding": "Utf8", + "eocd": { + "dir": { + "inner": { + "dir_disk_nbr": 0, + "dir_records_this_disk": 2, + "directory_offset": 954, + "directory_records": 2, + "directory_size": 168, + "disk_nbr": 0 + }, + "offset": 1122 + }, + "dir64": null, + "global_offset": 0 + }, + "parsed_ranges": [ + { + "contains": "end of central directory record", + "end": 1170, + "start": 1122 + }, + { + "contains": "central directory header", + "end": 1032, + "filename": "test.txt", + "start": 954 + }, + { + "contains": "central directory header", + "end": 1122, + "filename": "gophercolor16x16.png", + "start": 1032 + }, + { + "contains": "local file header", + "end": 66, + "filename": "test.txt", + "start": 0 + }, + { + "contains": "local file header", + "end": 169, + "filename": "gophercolor16x16.png", + "start": 91 + }, + { + "contains": "file data", + "end": 954, + "filename": "gophercolor16x16.png", + "start": 169 + } + ], + "size": 1170 +}