@@ -1082,6 +1082,26 @@ def test_generated_valid_zip64_extra(self):
1082
1082
1083
1083
def test_force_zip64 (self ):
1084
1084
"""Test that forcing zip64 extensions correctly notes this in the zip file"""
1085
+
1086
+ # GH-103861 describes an issue where forcing a small file to use zip64
1087
+ # extensions would add a zip64 extra record, but not change the
1088
+ # compressed lengths to 0xFFFFFFFF to indicate to the extractor that
1089
+ # the zip64 record should be read. Additionally, it would not set the
1090
+ # required version to indicate that zip64 extensions are required to
1091
+ # extract it.
1092
+ # This test replicates the situation and reads the raw data to specifically ensure:
1093
+ # - The required extract version is always >= ZIP64_VERSION
1094
+ # - The compressed and uncompressed size in the file headers are both
1095
+ # 0xFFFFFFFF (ie. point to zip64 record)
1096
+ # - The zip64 record is provided and has the correct sizes in it
1097
+ # Other aspects of the zip are checked as well, but verifying the above is the main goal.
1098
+ # Because this is hard to verify by parsing the data as a zip, the raw
1099
+ # bytes are checked to ensure that they line up with the zip spec.
1100
+ # The spec for this can be found at: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
1101
+ # The relevent sections for this test are:
1102
+ # - 4.3.7 for local file header
1103
+ # - 4.5.3 for zip64 extra field
1104
+
1085
1105
data = io .BytesIO ()
1086
1106
with zipfile .ZipFile (data , mode = "w" , allowZip64 = True ) as zf :
1087
1107
with zf .open ("text.txt" , mode = "w" , force_zip64 = True ) as zi :
@@ -1141,6 +1161,19 @@ def make_zip(fp):
1141
1161
def test_unseekable_zip_known_filesize (self ):
1142
1162
"""Test that creating a zip without seeking will use zip64 extensions if the file size is provided up-front"""
1143
1163
1164
+ # This test ensures that the zip will use a zip64 data descriptor (same
1165
+ # as a regular data descriptor except the sizes are 8 bytes instead of
1166
+ # 4) record to communicate the size of a file if the zip is being
1167
+ # written to an unseekable stream.
1168
+ # Because this sort of thing is hard to verify by parsing the data back
1169
+ # in as a zip, this test looks at the raw bytes created to ensure that
1170
+ # the correct data has been generated.
1171
+ # The spec for this can be found at: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
1172
+ # The relevent sections for this test are:
1173
+ # - 4.3.7 for local file header
1174
+ # - 4.3.9 for the data descriptor
1175
+ # - 4.5.3 for zip64 extra field
1176
+
1144
1177
file_size = zipfile .ZIP64_LIMIT + 1
1145
1178
1146
1179
def make_zip (fp ):
@@ -1160,7 +1193,7 @@ def make_zip(fp):
1160
1193
cd_sig
1161
1194
) = struct .unpack ("<4sBBHH8xIIHH8shhQQ{}x4s" .format (file_size ), seekable_data [:62 + file_size ])
1162
1195
1163
- self .assertEqual (header , b"PK\x03 \x04 " )
1196
+ self .assertEqual (header , b"PK\x03 \x04 " ) # local file header
1164
1197
self .assertGreaterEqual (vers , zipfile .ZIP64_VERSION ) # requires zip64 to extract
1165
1198
self .assertEqual (os , 0 ) # compatible with MS-DOS
1166
1199
self .assertEqual (flags , 0 ) # no flags set
@@ -1183,7 +1216,7 @@ def make_zip(fp):
1183
1216
dd_header , dd_usize , dd_csize , cd_sig
1184
1217
) = struct .unpack ("<4sBBHH8xIIHH8shhQQ{}x4s4xQQ4s" .format (file_size ), unseekable_data [:86 + file_size ])
1185
1218
1186
- self .assertEqual (header , b"PK\x03 \x04 " )
1219
+ self .assertEqual (header , b"PK\x03 \x04 " ) # local file header
1187
1220
self .assertGreaterEqual (vers , zipfile .ZIP64_VERSION ) # requires zip64 to extract
1188
1221
self .assertEqual (os , 0 ) # compatible with MS-DOS
1189
1222
self .assertEqual ("{:b}" .format (flags ), "1000" ) # streaming flag set
0 commit comments