zipremove 0.3.0__tar.gz → 0.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {zipremove-0.3.0/src/zipremove.egg-info → zipremove-0.4.1}/PKG-INFO +17 -14
- {zipremove-0.3.0 → zipremove-0.4.1}/README.md +16 -13
- {zipremove-0.3.0 → zipremove-0.4.1}/setup.cfg +1 -1
- {zipremove-0.3.0 → zipremove-0.4.1}/src/zipremove/__init__.py +32 -25
- {zipremove-0.3.0 → zipremove-0.4.1/src/zipremove.egg-info}/PKG-INFO +17 -14
- {zipremove-0.3.0 → zipremove-0.4.1}/tests/test_zipfile.py +435 -275
- {zipremove-0.3.0 → zipremove-0.4.1}/tests/test_zipfile64.py +9 -6
- {zipremove-0.3.0 → zipremove-0.4.1}/LICENSE.txt +0 -0
- {zipremove-0.3.0 → zipremove-0.4.1}/pyproject.toml +0 -0
- {zipremove-0.3.0 → zipremove-0.4.1}/setup.py +0 -0
- {zipremove-0.3.0 → zipremove-0.4.1}/src/zipremove.egg-info/SOURCES.txt +0 -0
- {zipremove-0.3.0 → zipremove-0.4.1}/src/zipremove.egg-info/dependency_links.txt +0 -0
- {zipremove-0.3.0 → zipremove-0.4.1}/src/zipremove.egg-info/requires.txt +0 -0
- {zipremove-0.3.0 → zipremove-0.4.1}/src/zipremove.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zipremove
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Extend `zipfile` with `remove`-related functionalities
|
|
5
5
|
Home-page: https://github.com/danny0838/zipremove
|
|
6
6
|
Author: Danny Lin
|
|
@@ -37,6 +37,7 @@ Dynamic: license-file
|
|
|
37
37
|

|
|
38
38
|

|
|
39
39
|
[](https://pepy.tech/project/zipremove)
|
|
40
|
+
[](https://github.com/python/cpython/pull/134627)
|
|
40
41
|
|
|
41
42
|
This package extends `zipfile` with `remove`-related functionalities.
|
|
42
43
|
|
|
@@ -61,30 +62,32 @@ This package extends `zipfile` with `remove`-related functionalities.
|
|
|
61
62
|
|
|
62
63
|
* `ZipFile.repack(removed=None, *, strict_descriptor=False[, chunk_size])`
|
|
63
64
|
|
|
64
|
-
Rewrites the archive to remove stale local file entries, shrinking
|
|
65
|
-
|
|
65
|
+
Rewrites the archive to remove stale local file entries, shrinking its file
|
|
66
|
+
size.
|
|
66
67
|
|
|
67
68
|
If *removed* is provided, it must be a sequence of `ZipInfo` objects
|
|
68
69
|
representing removed entries; only their corresponding local file entries
|
|
69
70
|
will be removed.
|
|
70
71
|
|
|
71
|
-
If *removed* is not provided,
|
|
72
|
-
|
|
73
|
-
entries
|
|
72
|
+
If *removed* is not provided, the archive is scanned to identify and remove
|
|
73
|
+
local file entries that are no longer referenced in the central directory.
|
|
74
|
+
The algorithm assumes that local file entries (and the central directory,
|
|
75
|
+
which is mostly treated as the "last entry") are stored consecutively:
|
|
74
76
|
|
|
75
77
|
1. Data before the first referenced entry is removed only when it appears to
|
|
76
78
|
be a sequence of consecutive entries with no extra following bytes; extra
|
|
77
|
-
|
|
79
|
+
preceding bytes are preserved.
|
|
78
80
|
2. Data between referenced entries is removed only when it appears to
|
|
79
81
|
be a sequence of consecutive entries with no extra preceding bytes; extra
|
|
80
82
|
following bytes are preserved.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
be
|
|
83
|
+
3. Entries must not overlap. If any entry's data overlaps with another, a
|
|
84
|
+
`BadZipFile` error is raised and no changes are made.
|
|
85
|
+
|
|
86
|
+
When scanning, setting `strict_descriptor=True` disables detection of any
|
|
87
|
+
entry using an unsigned data descriptor (deprecated in the ZIP specification
|
|
88
|
+
since version 6.3.0, released on 2006-09-29, and used only by some legacy
|
|
89
|
+
tools). This improves performance, but may cause some stale entries to be
|
|
90
|
+
preserved.
|
|
88
91
|
|
|
89
92
|
*chunk_size* may be specified to control the buffer size when moving
|
|
90
93
|
entry data (default is 1 MiB).
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|

|
|
4
4
|

|
|
5
5
|
[](https://pepy.tech/project/zipremove)
|
|
6
|
+
[](https://github.com/python/cpython/pull/134627)
|
|
6
7
|
|
|
7
8
|
This package extends `zipfile` with `remove`-related functionalities.
|
|
8
9
|
|
|
@@ -27,30 +28,32 @@ This package extends `zipfile` with `remove`-related functionalities.
|
|
|
27
28
|
|
|
28
29
|
* `ZipFile.repack(removed=None, *, strict_descriptor=False[, chunk_size])`
|
|
29
30
|
|
|
30
|
-
Rewrites the archive to remove stale local file entries, shrinking
|
|
31
|
-
|
|
31
|
+
Rewrites the archive to remove stale local file entries, shrinking its file
|
|
32
|
+
size.
|
|
32
33
|
|
|
33
34
|
If *removed* is provided, it must be a sequence of `ZipInfo` objects
|
|
34
35
|
representing removed entries; only their corresponding local file entries
|
|
35
36
|
will be removed.
|
|
36
37
|
|
|
37
|
-
If *removed* is not provided,
|
|
38
|
-
|
|
39
|
-
entries
|
|
38
|
+
If *removed* is not provided, the archive is scanned to identify and remove
|
|
39
|
+
local file entries that are no longer referenced in the central directory.
|
|
40
|
+
The algorithm assumes that local file entries (and the central directory,
|
|
41
|
+
which is mostly treated as the "last entry") are stored consecutively:
|
|
40
42
|
|
|
41
43
|
1. Data before the first referenced entry is removed only when it appears to
|
|
42
44
|
be a sequence of consecutive entries with no extra following bytes; extra
|
|
43
|
-
|
|
45
|
+
preceding bytes are preserved.
|
|
44
46
|
2. Data between referenced entries is removed only when it appears to
|
|
45
47
|
be a sequence of consecutive entries with no extra preceding bytes; extra
|
|
46
48
|
following bytes are preserved.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
be
|
|
49
|
+
3. Entries must not overlap. If any entry's data overlaps with another, a
|
|
50
|
+
`BadZipFile` error is raised and no changes are made.
|
|
51
|
+
|
|
52
|
+
When scanning, setting `strict_descriptor=True` disables detection of any
|
|
53
|
+
entry using an unsigned data descriptor (deprecated in the ZIP specification
|
|
54
|
+
since version 6.3.0, released on 2006-09-29, and used only by some legacy
|
|
55
|
+
tools). This improves performance, but may cause some stale entries to be
|
|
56
|
+
preserved.
|
|
54
57
|
|
|
55
58
|
*chunk_size* may be specified to control the buffer size when moving
|
|
56
59
|
entry data (default is 1 MiB).
|
|
@@ -15,6 +15,7 @@ from zipfile import (
|
|
|
15
15
|
_FH_GENERAL_PURPOSE_FLAG_BITS,
|
|
16
16
|
_FH_SIGNATURE,
|
|
17
17
|
_FH_UNCOMPRESSED_SIZE,
|
|
18
|
+
LZMADecompressor,
|
|
18
19
|
_get_decompressor,
|
|
19
20
|
crc32,
|
|
20
21
|
sizeFileHeader,
|
|
@@ -29,6 +30,12 @@ except NameError:
|
|
|
29
30
|
# polyfill for Python < 3.14
|
|
30
31
|
ZIP_ZSTANDARD = 93
|
|
31
32
|
|
|
33
|
+
try:
|
|
34
|
+
from zipfile import _MASK_ENCRYPTED
|
|
35
|
+
except ImportError:
|
|
36
|
+
# polyfill for Python < 3.11
|
|
37
|
+
_MASK_ENCRYPTED = 1 << 0
|
|
38
|
+
|
|
32
39
|
try:
|
|
33
40
|
from zipfile import _MASK_USE_DATA_DESCRIPTOR
|
|
34
41
|
except ImportError:
|
|
@@ -50,18 +57,16 @@ except ImportError:
|
|
|
50
57
|
return filename
|
|
51
58
|
|
|
52
59
|
try:
|
|
53
|
-
|
|
54
|
-
zipfile.LZMADecompressor().unused_data
|
|
60
|
+
LZMADecompressor().unused_data
|
|
55
61
|
except AttributeError:
|
|
56
62
|
# polyfill to support LZMADecompressor().unused_data
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
zipfile.LZMADecompressor = LZMADecompressor
|
|
63
|
+
@property
|
|
64
|
+
def unused_data(self):
|
|
65
|
+
try:
|
|
66
|
+
return self._decomp.unused_data
|
|
67
|
+
except AttributeError:
|
|
68
|
+
return b''
|
|
69
|
+
LZMADecompressor.unused_data = unused_data
|
|
65
70
|
|
|
66
71
|
|
|
67
72
|
class _ZipRepacker:
|
|
@@ -257,14 +262,11 @@ class _ZipRepacker:
|
|
|
257
262
|
used_entry_size,
|
|
258
263
|
)
|
|
259
264
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
)
|
|
266
|
-
else:
|
|
267
|
-
stale_entry_size = 0
|
|
265
|
+
stale_entry_size = self._validate_local_file_entry_sequence(
|
|
266
|
+
fp,
|
|
267
|
+
old_header_offset + used_entry_size,
|
|
268
|
+
old_header_offset + entry_size,
|
|
269
|
+
)
|
|
268
270
|
|
|
269
271
|
if stale_entry_size > 0:
|
|
270
272
|
self._copy_bytes(
|
|
@@ -375,8 +377,8 @@ class _ZipRepacker:
|
|
|
375
377
|
if pos > end_offset:
|
|
376
378
|
return None
|
|
377
379
|
|
|
380
|
+
# parse zip64
|
|
378
381
|
try:
|
|
379
|
-
# parse zip64
|
|
380
382
|
try:
|
|
381
383
|
zinfo._decodeExtra(crc32(filename))
|
|
382
384
|
except TypeError:
|
|
@@ -399,8 +401,11 @@ class _ZipRepacker:
|
|
|
399
401
|
|
|
400
402
|
dd = self._scan_data_descriptor(fp, pos, end_offset, zip64)
|
|
401
403
|
if dd is None and not self.strict_descriptor:
|
|
402
|
-
|
|
403
|
-
|
|
404
|
+
if zinfo.flag_bits & _MASK_ENCRYPTED:
|
|
405
|
+
dd = False
|
|
406
|
+
else:
|
|
407
|
+
dd = self._scan_data_descriptor_no_sig_by_decompression(
|
|
408
|
+
fp, pos, end_offset, zip64, fheader[_FH_COMPRESSION_METHOD])
|
|
404
409
|
if dd is False:
|
|
405
410
|
dd = self._scan_data_descriptor_no_sig(fp, pos, end_offset, zip64)
|
|
406
411
|
if dd is None:
|
|
@@ -488,6 +493,7 @@ class _ZipRepacker:
|
|
|
488
493
|
dd_fmt = '<LQQ' if zip64 else '<LLL'
|
|
489
494
|
dd_size = struct.calcsize(dd_fmt)
|
|
490
495
|
|
|
496
|
+
# early return and prevent potential `fp.read(-1)`
|
|
491
497
|
if end_offset - dd_size < offset:
|
|
492
498
|
return None
|
|
493
499
|
|
|
@@ -608,15 +614,16 @@ class ZipFile(ZipFile):
|
|
|
608
614
|
|
|
609
615
|
with self._lock:
|
|
610
616
|
# get the zinfo
|
|
611
|
-
# raise KeyError if arcname does not exist
|
|
612
617
|
if isinstance(zinfo_or_arcname, ZipInfo):
|
|
613
618
|
zinfo = zinfo_or_arcname
|
|
614
|
-
if zinfo not in self.filelist:
|
|
615
|
-
raise KeyError('There is no item %r in the archive' % zinfo)
|
|
616
619
|
else:
|
|
620
|
+
# raise KeyError if arcname does not exist
|
|
617
621
|
zinfo = self.getinfo(zinfo_or_arcname)
|
|
618
622
|
|
|
619
|
-
|
|
623
|
+
try:
|
|
624
|
+
self.filelist.remove(zinfo)
|
|
625
|
+
except ValueError:
|
|
626
|
+
raise KeyError('There is no item %r in the archive' % zinfo) from None
|
|
620
627
|
|
|
621
628
|
try:
|
|
622
629
|
del self.NameToInfo[zinfo.filename]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zipremove
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Extend `zipfile` with `remove`-related functionalities
|
|
5
5
|
Home-page: https://github.com/danny0838/zipremove
|
|
6
6
|
Author: Danny Lin
|
|
@@ -37,6 +37,7 @@ Dynamic: license-file
|
|
|
37
37
|

|
|
38
38
|

|
|
39
39
|
[](https://pepy.tech/project/zipremove)
|
|
40
|
+
[](https://github.com/python/cpython/pull/134627)
|
|
40
41
|
|
|
41
42
|
This package extends `zipfile` with `remove`-related functionalities.
|
|
42
43
|
|
|
@@ -61,30 +62,32 @@ This package extends `zipfile` with `remove`-related functionalities.
|
|
|
61
62
|
|
|
62
63
|
* `ZipFile.repack(removed=None, *, strict_descriptor=False[, chunk_size])`
|
|
63
64
|
|
|
64
|
-
Rewrites the archive to remove stale local file entries, shrinking
|
|
65
|
-
|
|
65
|
+
Rewrites the archive to remove stale local file entries, shrinking its file
|
|
66
|
+
size.
|
|
66
67
|
|
|
67
68
|
If *removed* is provided, it must be a sequence of `ZipInfo` objects
|
|
68
69
|
representing removed entries; only their corresponding local file entries
|
|
69
70
|
will be removed.
|
|
70
71
|
|
|
71
|
-
If *removed* is not provided,
|
|
72
|
-
|
|
73
|
-
entries
|
|
72
|
+
If *removed* is not provided, the archive is scanned to identify and remove
|
|
73
|
+
local file entries that are no longer referenced in the central directory.
|
|
74
|
+
The algorithm assumes that local file entries (and the central directory,
|
|
75
|
+
which is mostly treated as the "last entry") are stored consecutively:
|
|
74
76
|
|
|
75
77
|
1. Data before the first referenced entry is removed only when it appears to
|
|
76
78
|
be a sequence of consecutive entries with no extra following bytes; extra
|
|
77
|
-
|
|
79
|
+
preceding bytes are preserved.
|
|
78
80
|
2. Data between referenced entries is removed only when it appears to
|
|
79
81
|
be a sequence of consecutive entries with no extra preceding bytes; extra
|
|
80
82
|
following bytes are preserved.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
be
|
|
83
|
+
3. Entries must not overlap. If any entry's data overlaps with another, a
|
|
84
|
+
`BadZipFile` error is raised and no changes are made.
|
|
85
|
+
|
|
86
|
+
When scanning, setting `strict_descriptor=True` disables detection of any
|
|
87
|
+
entry using an unsigned data descriptor (deprecated in the ZIP specification
|
|
88
|
+
since version 6.3.0, released on 2006-09-29, and used only by some legacy
|
|
89
|
+
tools). This improves performance, but may cause some stale entries to be
|
|
90
|
+
preserved.
|
|
88
91
|
|
|
89
92
|
*chunk_size* may be specified to control the buffer size when moving
|
|
90
93
|
entry data (default is 1 MiB).
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import io
|
|
2
3
|
import itertools
|
|
3
4
|
import os
|
|
@@ -610,6 +611,25 @@ class AbstractRepackTests(RepackHelperMixin):
|
|
|
610
611
|
with zipfile.ZipFile(TESTFN) as zh:
|
|
611
612
|
self.assertIsNone(zh.testzip())
|
|
612
613
|
|
|
614
|
+
def test_repack_propagation(self):
|
|
615
|
+
"""Should call internal API with adequate parameters."""
|
|
616
|
+
self._prepare_zip_from_test_files(TESTFN, self.test_files)
|
|
617
|
+
|
|
618
|
+
with zipfile.ZipFile(TESTFN, 'a', self.compression) as zh:
|
|
619
|
+
with mock.patch.object(zipfile._ZipRepacker, 'repack') as m_rp, \
|
|
620
|
+
mock.patch.object(zipfile, '_ZipRepacker', wraps=zipfile._ZipRepacker) as m_zr:
|
|
621
|
+
zh.repack()
|
|
622
|
+
m_zr.assert_called_once_with()
|
|
623
|
+
m_rp.assert_called_once_with(zh, None)
|
|
624
|
+
|
|
625
|
+
with zipfile.ZipFile(TESTFN, 'a', self.compression) as zh:
|
|
626
|
+
zi = zh.remove(zh.infolist()[0])
|
|
627
|
+
with mock.patch.object(zipfile._ZipRepacker, 'repack') as m_rp, \
|
|
628
|
+
mock.patch.object(zipfile, '_ZipRepacker', wraps=zipfile._ZipRepacker) as m_zr:
|
|
629
|
+
zh.repack([zi], strict_descriptor=True, chunk_size=1024)
|
|
630
|
+
m_zr.assert_called_once_with(strict_descriptor=True, chunk_size=1024)
|
|
631
|
+
m_rp.assert_called_once_with(zh, [zi])
|
|
632
|
+
|
|
613
633
|
def test_repack_bytes_before_first_file(self):
|
|
614
634
|
"""Should preserve random bytes before the first recorded local file entry."""
|
|
615
635
|
for ii in ([], [0], [0, 1], [0, 1, 2]):
|
|
@@ -847,222 +867,6 @@ class AbstractRepackTests(RepackHelperMixin):
|
|
|
847
867
|
with zipfile.ZipFile(TESTFN) as zh:
|
|
848
868
|
self.assertIsNone(zh.testzip())
|
|
849
869
|
|
|
850
|
-
@requires_zip64fix()
|
|
851
|
-
def test_repack_zip64(self):
|
|
852
|
-
"""Should correctly handle file entries with zip64."""
|
|
853
|
-
for ii in ([0], [0, 1], [1], [2]):
|
|
854
|
-
with self.subTest(remove=ii):
|
|
855
|
-
# calculate the expected results
|
|
856
|
-
test_files = [data for j, data in enumerate(self.test_files) if j not in ii]
|
|
857
|
-
expected_zinfos = self._prepare_zip_from_test_files(TESTFN, test_files, force_zip64=True)
|
|
858
|
-
expected_size = os.path.getsize(TESTFN)
|
|
859
|
-
|
|
860
|
-
# do the removal and check the result
|
|
861
|
-
self._prepare_zip_from_test_files(TESTFN, self.test_files, force_zip64=True)
|
|
862
|
-
with zipfile.ZipFile(TESTFN, 'a', self.compression) as zh:
|
|
863
|
-
for i in ii:
|
|
864
|
-
zh.remove(self.test_files[i][0])
|
|
865
|
-
zh.repack()
|
|
866
|
-
|
|
867
|
-
# check infolist
|
|
868
|
-
self.assertEqual(
|
|
869
|
-
[ComparableZipInfo(zi) for zi in zh.infolist()],
|
|
870
|
-
expected_zinfos,
|
|
871
|
-
)
|
|
872
|
-
|
|
873
|
-
# check file size
|
|
874
|
-
self.assertEqual(os.path.getsize(TESTFN), expected_size)
|
|
875
|
-
|
|
876
|
-
# make sure the zip file is still valid
|
|
877
|
-
with zipfile.ZipFile(TESTFN) as zh:
|
|
878
|
-
self.assertIsNone(zh.testzip())
|
|
879
|
-
|
|
880
|
-
def test_repack_data_descriptor(self):
|
|
881
|
-
"""Should correctly handle file entries using data descriptor."""
|
|
882
|
-
for ii in ([0], [0, 1], [1], [2]):
|
|
883
|
-
with self.subTest(remove=ii):
|
|
884
|
-
# calculate the expected results
|
|
885
|
-
test_files = [data for j, data in enumerate(self.test_files) if j not in ii]
|
|
886
|
-
with open(TESTFN, 'wb') as fh:
|
|
887
|
-
expected_zinfos = self._prepare_zip_from_test_files(Unseekable(fh), test_files)
|
|
888
|
-
expected_size = os.path.getsize(TESTFN)
|
|
889
|
-
|
|
890
|
-
# do the removal and check the result
|
|
891
|
-
with open(TESTFN, 'wb') as fh:
|
|
892
|
-
self._prepare_zip_from_test_files(Unseekable(fh), self.test_files)
|
|
893
|
-
with zipfile.ZipFile(TESTFN, 'a', self.compression) as zh:
|
|
894
|
-
# make sure data descriptor bit is really set (by making zipfile unseekable)
|
|
895
|
-
for zi in zh.infolist():
|
|
896
|
-
self.assertTrue(zi.flag_bits & 8, f'data descriptor not used: {zi.filename}')
|
|
897
|
-
|
|
898
|
-
for i in ii:
|
|
899
|
-
zh.remove(self.test_files[i][0])
|
|
900
|
-
zh.repack()
|
|
901
|
-
|
|
902
|
-
# check infolist
|
|
903
|
-
self.assertEqual(
|
|
904
|
-
[ComparableZipInfo(zi) for zi in zh.infolist()],
|
|
905
|
-
expected_zinfos,
|
|
906
|
-
)
|
|
907
|
-
|
|
908
|
-
# check file size
|
|
909
|
-
self.assertEqual(os.path.getsize(TESTFN), expected_size)
|
|
910
|
-
|
|
911
|
-
# make sure the zip file is still valid
|
|
912
|
-
with zipfile.ZipFile(TESTFN) as zh:
|
|
913
|
-
self.assertIsNone(zh.testzip())
|
|
914
|
-
|
|
915
|
-
@requires_zip64fix()
|
|
916
|
-
def test_repack_data_descriptor_and_zip64(self):
|
|
917
|
-
"""Should correctly handle file entries using data descriptor and zip64."""
|
|
918
|
-
for ii in ([0], [0, 1], [1], [2]):
|
|
919
|
-
with self.subTest(remove=ii):
|
|
920
|
-
# calculate the expected results
|
|
921
|
-
test_files = [data for j, data in enumerate(self.test_files) if j not in ii]
|
|
922
|
-
with open(TESTFN, 'wb') as fh:
|
|
923
|
-
expected_zinfos = self._prepare_zip_from_test_files(Unseekable(fh), test_files, force_zip64=True)
|
|
924
|
-
expected_size = os.path.getsize(TESTFN)
|
|
925
|
-
|
|
926
|
-
# do the removal and check the result
|
|
927
|
-
with open(TESTFN, 'wb') as fh:
|
|
928
|
-
self._prepare_zip_from_test_files(Unseekable(fh), self.test_files, force_zip64=True)
|
|
929
|
-
with zipfile.ZipFile(TESTFN, 'a', self.compression) as zh:
|
|
930
|
-
# make sure data descriptor bit is really set (by making zipfile unseekable)
|
|
931
|
-
for zi in zh.infolist():
|
|
932
|
-
self.assertTrue(zi.flag_bits & 8, f'data descriptor not used: {zi.filename}')
|
|
933
|
-
|
|
934
|
-
for i in ii:
|
|
935
|
-
zh.remove(self.test_files[i][0])
|
|
936
|
-
zh.repack()
|
|
937
|
-
|
|
938
|
-
# check infolist
|
|
939
|
-
self.assertEqual(
|
|
940
|
-
[ComparableZipInfo(zi) for zi in zh.infolist()],
|
|
941
|
-
expected_zinfos,
|
|
942
|
-
)
|
|
943
|
-
|
|
944
|
-
# check file size
|
|
945
|
-
self.assertEqual(os.path.getsize(TESTFN), expected_size)
|
|
946
|
-
|
|
947
|
-
# make sure the zip file is still valid
|
|
948
|
-
with zipfile.ZipFile(TESTFN) as zh:
|
|
949
|
-
self.assertIsNone(zh.testzip())
|
|
950
|
-
|
|
951
|
-
def test_repack_data_descriptor_no_sig(self):
|
|
952
|
-
"""Should correctly handle file entries using data descriptor without signature."""
|
|
953
|
-
for ii in ([0], [0, 1], [1], [2]):
|
|
954
|
-
with self.subTest(remove=ii):
|
|
955
|
-
# calculate the expected results
|
|
956
|
-
test_files = [data for j, data in enumerate(self.test_files) if j not in ii]
|
|
957
|
-
with open(TESTFN, 'wb') as fh:
|
|
958
|
-
with mock.patch.object(struct, 'pack', side_effect=struct_pack_no_dd_sig):
|
|
959
|
-
expected_zinfos = self._prepare_zip_from_test_files(Unseekable(fh), test_files)
|
|
960
|
-
expected_size = os.path.getsize(TESTFN)
|
|
961
|
-
|
|
962
|
-
# do the removal and check the result
|
|
963
|
-
with open(TESTFN, 'wb') as fh:
|
|
964
|
-
with mock.patch.object(struct, 'pack', side_effect=struct_pack_no_dd_sig):
|
|
965
|
-
self._prepare_zip_from_test_files(Unseekable(fh), self.test_files)
|
|
966
|
-
with zipfile.ZipFile(TESTFN, 'a', self.compression) as zh:
|
|
967
|
-
# make sure data descriptor bit is really set (by making zipfile unseekable)
|
|
968
|
-
for zi in zh.infolist():
|
|
969
|
-
self.assertTrue(zi.flag_bits & 8, f'data descriptor flag not set: {zi.filename}')
|
|
970
|
-
|
|
971
|
-
for i in ii:
|
|
972
|
-
zh.remove(self.test_files[i][0])
|
|
973
|
-
zh.repack()
|
|
974
|
-
|
|
975
|
-
# check infolist
|
|
976
|
-
self.assertEqual(
|
|
977
|
-
[ComparableZipInfo(zi) for zi in zh.infolist()],
|
|
978
|
-
expected_zinfos,
|
|
979
|
-
)
|
|
980
|
-
|
|
981
|
-
# check file size
|
|
982
|
-
self.assertEqual(os.path.getsize(TESTFN), expected_size)
|
|
983
|
-
|
|
984
|
-
# make sure the zip file is still valid
|
|
985
|
-
with zipfile.ZipFile(TESTFN) as zh:
|
|
986
|
-
self.assertIsNone(zh.testzip())
|
|
987
|
-
|
|
988
|
-
def test_repack_data_descriptor_no_sig_strict(self):
|
|
989
|
-
"""Should skip data descriptor without signature when `strict_descriptor` is set."""
|
|
990
|
-
for ii in ([0], [0, 1], [1], [2]):
|
|
991
|
-
with self.subTest(remove=ii):
|
|
992
|
-
# calculate the expected results
|
|
993
|
-
with open(TESTFN, 'wb') as fh:
|
|
994
|
-
with mock.patch.object(struct, 'pack', side_effect=struct_pack_no_dd_sig):
|
|
995
|
-
self._prepare_zip_from_test_files(Unseekable(fh), self.test_files)
|
|
996
|
-
with zipfile.ZipFile(TESTFN, 'a') as zh:
|
|
997
|
-
for i in ii:
|
|
998
|
-
zh.remove(self.test_files[i][0])
|
|
999
|
-
expected_zinfos = [ComparableZipInfo(zi) for zi in zh.infolist()]
|
|
1000
|
-
expected_size = os.path.getsize(TESTFN)
|
|
1001
|
-
|
|
1002
|
-
# do the removal and check the result
|
|
1003
|
-
with open(TESTFN, 'wb') as fh:
|
|
1004
|
-
with mock.patch.object(struct, 'pack', side_effect=struct_pack_no_dd_sig):
|
|
1005
|
-
self._prepare_zip_from_test_files(Unseekable(fh), self.test_files)
|
|
1006
|
-
with zipfile.ZipFile(TESTFN, 'a', self.compression) as zh:
|
|
1007
|
-
# make sure data descriptor bit is really set (by making zipfile unseekable)
|
|
1008
|
-
for zi in zh.infolist():
|
|
1009
|
-
self.assertTrue(zi.flag_bits & 8, f'data descriptor flag not set: {zi.filename}')
|
|
1010
|
-
|
|
1011
|
-
for i in ii:
|
|
1012
|
-
zh.remove(self.test_files[i][0])
|
|
1013
|
-
zh.repack(strict_descriptor=True)
|
|
1014
|
-
|
|
1015
|
-
# check infolist
|
|
1016
|
-
self.assertEqual(
|
|
1017
|
-
[ComparableZipInfo(zi) for zi in zh.infolist()],
|
|
1018
|
-
expected_zinfos,
|
|
1019
|
-
)
|
|
1020
|
-
|
|
1021
|
-
# check file size
|
|
1022
|
-
self.assertEqual(os.path.getsize(TESTFN), expected_size)
|
|
1023
|
-
|
|
1024
|
-
# make sure the zip file is still valid
|
|
1025
|
-
with zipfile.ZipFile(TESTFN) as zh:
|
|
1026
|
-
self.assertIsNone(zh.testzip())
|
|
1027
|
-
|
|
1028
|
-
@requires_zip64fix()
|
|
1029
|
-
def test_repack_data_descriptor_no_sig_and_zip64(self):
|
|
1030
|
-
"""Should correctly handle file entries using data descriptor without signature and zip64."""
|
|
1031
|
-
for ii in ([0], [0, 1], [1], [2]):
|
|
1032
|
-
with self.subTest(remove=ii):
|
|
1033
|
-
# calculate the expected results
|
|
1034
|
-
test_files = [data for j, data in enumerate(self.test_files) if j not in ii]
|
|
1035
|
-
with open(TESTFN, 'wb') as fh:
|
|
1036
|
-
with mock.patch.object(struct, 'pack', side_effect=struct_pack_no_dd_sig):
|
|
1037
|
-
expected_zinfos = self._prepare_zip_from_test_files(Unseekable(fh), test_files, force_zip64=True)
|
|
1038
|
-
expected_size = os.path.getsize(TESTFN)
|
|
1039
|
-
|
|
1040
|
-
# do the removal and check the result
|
|
1041
|
-
with open(TESTFN, 'wb') as fh:
|
|
1042
|
-
with mock.patch.object(struct, 'pack', side_effect=struct_pack_no_dd_sig):
|
|
1043
|
-
self._prepare_zip_from_test_files(Unseekable(fh), self.test_files, force_zip64=True)
|
|
1044
|
-
with zipfile.ZipFile(TESTFN, 'a', self.compression) as zh:
|
|
1045
|
-
# make sure data descriptor bit is really set (by making zipfile unseekable)
|
|
1046
|
-
for zi in zh.infolist():
|
|
1047
|
-
self.assertTrue(zi.flag_bits & 8, f'data descriptor flag not set: {zi.filename}')
|
|
1048
|
-
|
|
1049
|
-
for i in ii:
|
|
1050
|
-
zh.remove(self.test_files[i][0])
|
|
1051
|
-
zh.repack()
|
|
1052
|
-
|
|
1053
|
-
# check infolist
|
|
1054
|
-
self.assertEqual(
|
|
1055
|
-
[ComparableZipInfo(zi) for zi in zh.infolist()],
|
|
1056
|
-
expected_zinfos,
|
|
1057
|
-
)
|
|
1058
|
-
|
|
1059
|
-
# check file size
|
|
1060
|
-
self.assertEqual(os.path.getsize(TESTFN), expected_size)
|
|
1061
|
-
|
|
1062
|
-
# make sure the zip file is still valid
|
|
1063
|
-
with zipfile.ZipFile(TESTFN) as zh:
|
|
1064
|
-
self.assertIsNone(zh.testzip())
|
|
1065
|
-
|
|
1066
870
|
def test_repack_prepended_bytes(self):
|
|
1067
871
|
for ii in ([], [0], [0, 1], [1], [2]):
|
|
1068
872
|
with self.subTest(remove=ii):
|
|
@@ -1575,6 +1379,320 @@ class OtherRepackTests(unittest.TestCase):
|
|
|
1575
1379
|
self.assertEqual(fz.read(), expected)
|
|
1576
1380
|
|
|
1577
1381
|
class ZipRepackerTests(unittest.TestCase):
|
|
1382
|
+
def _generate_local_file_entry(self, arcname, raw_bytes,
|
|
1383
|
+
compression=zipfile.ZIP_STORED,
|
|
1384
|
+
force_zip64=False, dd=False, dd_sig=True):
|
|
1385
|
+
fz = io.BytesIO()
|
|
1386
|
+
f = Unseekable(fz) if dd else fz
|
|
1387
|
+
cm = (mock.patch.object(struct, 'pack', side_effect=struct_pack_no_dd_sig)
|
|
1388
|
+
if not dd_sig else contextlib.nullcontext())
|
|
1389
|
+
with zipfile.ZipFile(f, 'w', compression=compression) as zh:
|
|
1390
|
+
with cm:
|
|
1391
|
+
with zh.open(arcname, 'w', force_zip64=force_zip64) as fh:
|
|
1392
|
+
fh.write(raw_bytes)
|
|
1393
|
+
fz.seek(0)
|
|
1394
|
+
return fz.read()
|
|
1395
|
+
|
|
1396
|
+
def test_validate_local_file_entry_stored(self):
|
|
1397
|
+
self._test_validate_local_file_entry(method=zipfile.ZIP_STORED)
|
|
1398
|
+
|
|
1399
|
+
@requires_zlib()
|
|
1400
|
+
def test_validate_local_file_entry_zlib(self):
|
|
1401
|
+
self._test_validate_local_file_entry(method=zipfile.ZIP_DEFLATED)
|
|
1402
|
+
|
|
1403
|
+
@requires_bz2()
|
|
1404
|
+
def test_validate_local_file_entry_bz2(self):
|
|
1405
|
+
self._test_validate_local_file_entry(method=zipfile.ZIP_BZIP2)
|
|
1406
|
+
|
|
1407
|
+
@requires_lzma()
|
|
1408
|
+
def test_validate_local_file_entry_lzma(self):
|
|
1409
|
+
self._test_validate_local_file_entry(method=zipfile.ZIP_LZMA)
|
|
1410
|
+
|
|
1411
|
+
@requires_zstd()
|
|
1412
|
+
def test_validate_local_file_entry_zstd(self):
|
|
1413
|
+
self._test_validate_local_file_entry(method=zipfile.ZIP_ZSTANDARD)
|
|
1414
|
+
|
|
1415
|
+
def _test_validate_local_file_entry(self, method):
|
|
1416
|
+
repacker = zipfile._ZipRepacker()
|
|
1417
|
+
|
|
1418
|
+
# basic
|
|
1419
|
+
bytes_ = self._generate_local_file_entry(
|
|
1420
|
+
'file.txt', b'dummy', compression=method)
|
|
1421
|
+
fz = io.BytesIO(bytes_)
|
|
1422
|
+
with mock.patch.object(repacker, '_scan_data_descriptor',
|
|
1423
|
+
wraps=repacker._scan_data_descriptor) as m_sdd, \
|
|
1424
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig_by_decompression',
|
|
1425
|
+
wraps=repacker._scan_data_descriptor_no_sig_by_decompression) as m_sddnsbd, \
|
|
1426
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig',
|
|
1427
|
+
wraps=repacker._scan_data_descriptor_no_sig) as m_sddns:
|
|
1428
|
+
result = repacker._validate_local_file_entry(fz, 0, len(bytes_))
|
|
1429
|
+
self.assertEqual(result, len(bytes_))
|
|
1430
|
+
m_sdd.assert_not_called()
|
|
1431
|
+
m_sddnsbd.assert_not_called()
|
|
1432
|
+
m_sddns.assert_not_called()
|
|
1433
|
+
|
|
1434
|
+
# offset
|
|
1435
|
+
with mock.patch.object(repacker, '_scan_data_descriptor',
|
|
1436
|
+
wraps=repacker._scan_data_descriptor) as m_sdd, \
|
|
1437
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig_by_decompression',
|
|
1438
|
+
wraps=repacker._scan_data_descriptor_no_sig_by_decompression) as m_sddnsbd, \
|
|
1439
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig',
|
|
1440
|
+
wraps=repacker._scan_data_descriptor_no_sig) as m_sddns:
|
|
1441
|
+
result = repacker._validate_local_file_entry(fz, 0, len(bytes_) + 1)
|
|
1442
|
+
self.assertEqual(result, len(bytes_))
|
|
1443
|
+
m_sdd.assert_not_called()
|
|
1444
|
+
m_sddnsbd.assert_not_called()
|
|
1445
|
+
m_sddns.assert_not_called()
|
|
1446
|
+
|
|
1447
|
+
bytes_ = b'pre' + bytes_ + b'post'
|
|
1448
|
+
fz = io.BytesIO(bytes_)
|
|
1449
|
+
with mock.patch.object(repacker, '_scan_data_descriptor',
|
|
1450
|
+
wraps=repacker._scan_data_descriptor) as m_sdd, \
|
|
1451
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig_by_decompression',
|
|
1452
|
+
wraps=repacker._scan_data_descriptor_no_sig_by_decompression) as m_sddnsbd, \
|
|
1453
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig',
|
|
1454
|
+
wraps=repacker._scan_data_descriptor_no_sig) as m_sddns:
|
|
1455
|
+
result = repacker._validate_local_file_entry(fz, 3, len(bytes_) - 4)
|
|
1456
|
+
self.assertEqual(result, len(bytes_) - 7)
|
|
1457
|
+
m_sdd.assert_not_called()
|
|
1458
|
+
m_sddnsbd.assert_not_called()
|
|
1459
|
+
m_sddns.assert_not_called()
|
|
1460
|
+
|
|
1461
|
+
with mock.patch.object(repacker, '_scan_data_descriptor',
|
|
1462
|
+
wraps=repacker._scan_data_descriptor) as m_sdd, \
|
|
1463
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig_by_decompression',
|
|
1464
|
+
wraps=repacker._scan_data_descriptor_no_sig_by_decompression) as m_sddnsbd, \
|
|
1465
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig',
|
|
1466
|
+
wraps=repacker._scan_data_descriptor_no_sig) as m_sddns:
|
|
1467
|
+
result = repacker._validate_local_file_entry(fz, 3, len(bytes_))
|
|
1468
|
+
self.assertEqual(result, len(bytes_) - 7)
|
|
1469
|
+
m_sdd.assert_not_called()
|
|
1470
|
+
m_sddnsbd.assert_not_called()
|
|
1471
|
+
m_sddns.assert_not_called()
|
|
1472
|
+
|
|
1473
|
+
# return None if no match at given offset
|
|
1474
|
+
with mock.patch.object(repacker, '_scan_data_descriptor',
|
|
1475
|
+
wraps=repacker._scan_data_descriptor) as m_sdd, \
|
|
1476
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig_by_decompression',
|
|
1477
|
+
wraps=repacker._scan_data_descriptor_no_sig_by_decompression) as m_sddnsbd, \
|
|
1478
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig',
|
|
1479
|
+
wraps=repacker._scan_data_descriptor_no_sig) as m_sddns:
|
|
1480
|
+
result = repacker._validate_local_file_entry(fz, 2, len(bytes_) - 4)
|
|
1481
|
+
self.assertEqual(result, None)
|
|
1482
|
+
m_sdd.assert_not_called()
|
|
1483
|
+
m_sddnsbd.assert_not_called()
|
|
1484
|
+
m_sddns.assert_not_called()
|
|
1485
|
+
|
|
1486
|
+
with mock.patch.object(repacker, '_scan_data_descriptor',
|
|
1487
|
+
wraps=repacker._scan_data_descriptor) as m_sdd, \
|
|
1488
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig_by_decompression',
|
|
1489
|
+
wraps=repacker._scan_data_descriptor_no_sig_by_decompression) as m_sddnsbd, \
|
|
1490
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig',
|
|
1491
|
+
wraps=repacker._scan_data_descriptor_no_sig) as m_sddns:
|
|
1492
|
+
result = repacker._validate_local_file_entry(fz, 4, len(bytes_) - 4)
|
|
1493
|
+
self.assertEqual(result, None)
|
|
1494
|
+
m_sdd.assert_not_called()
|
|
1495
|
+
m_sddnsbd.assert_not_called()
|
|
1496
|
+
m_sddns.assert_not_called()
|
|
1497
|
+
|
|
1498
|
+
# return None if no sufficient header length
|
|
1499
|
+
bytes_ = self._generate_local_file_entry(
|
|
1500
|
+
'file.txt', b'dummy', compression=method)
|
|
1501
|
+
bytes_ = bytes_[:29]
|
|
1502
|
+
fz = io.BytesIO(bytes_)
|
|
1503
|
+
with mock.patch.object(repacker, '_scan_data_descriptor',
|
|
1504
|
+
wraps=repacker._scan_data_descriptor) as m_sdd, \
|
|
1505
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig_by_decompression',
|
|
1506
|
+
wraps=repacker._scan_data_descriptor_no_sig_by_decompression) as m_sddnsbd, \
|
|
1507
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig',
|
|
1508
|
+
wraps=repacker._scan_data_descriptor_no_sig) as m_sddns:
|
|
1509
|
+
result = repacker._validate_local_file_entry(fz, 0, len(bytes_))
|
|
1510
|
+
self.assertEqual(result, None)
|
|
1511
|
+
m_sdd.assert_not_called()
|
|
1512
|
+
m_sddnsbd.assert_not_called()
|
|
1513
|
+
m_sddns.assert_not_called()
|
|
1514
|
+
|
|
1515
|
+
# data descriptor
|
|
1516
|
+
bytes_ = self._generate_local_file_entry(
|
|
1517
|
+
'file.txt', b'dummy', compression=method, dd=True)
|
|
1518
|
+
fz = io.BytesIO(bytes_)
|
|
1519
|
+
with mock.patch.object(repacker, '_scan_data_descriptor',
|
|
1520
|
+
wraps=repacker._scan_data_descriptor) as m_sdd, \
|
|
1521
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig_by_decompression',
|
|
1522
|
+
wraps=repacker._scan_data_descriptor_no_sig_by_decompression) as m_sddnsbd, \
|
|
1523
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig',
|
|
1524
|
+
wraps=repacker._scan_data_descriptor_no_sig) as m_sddns:
|
|
1525
|
+
result = repacker._validate_local_file_entry(fz, 0, len(bytes_))
|
|
1526
|
+
self.assertEqual(result, len(bytes_))
|
|
1527
|
+
m_sdd.assert_called_once_with(fz, 38, len(bytes_), False)
|
|
1528
|
+
m_sddnsbd.assert_not_called()
|
|
1529
|
+
m_sddns.assert_not_called()
|
|
1530
|
+
|
|
1531
|
+
# data descriptor (unsigned)
|
|
1532
|
+
bytes_ = self._generate_local_file_entry(
|
|
1533
|
+
'file.txt', b'dummy', compression=method, dd=True, dd_sig=False)
|
|
1534
|
+
fz = io.BytesIO(bytes_)
|
|
1535
|
+
with mock.patch.object(repacker, '_scan_data_descriptor',
|
|
1536
|
+
wraps=repacker._scan_data_descriptor) as m_sdd, \
|
|
1537
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig_by_decompression',
|
|
1538
|
+
wraps=repacker._scan_data_descriptor_no_sig_by_decompression) as m_sddnsbd, \
|
|
1539
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig',
|
|
1540
|
+
wraps=repacker._scan_data_descriptor_no_sig) as m_sddns:
|
|
1541
|
+
result = repacker._validate_local_file_entry(fz, 0, len(bytes_))
|
|
1542
|
+
self.assertEqual(result, len(bytes_))
|
|
1543
|
+
m_sdd.assert_called_once_with(fz, 38, len(bytes_), False)
|
|
1544
|
+
m_sddnsbd.assert_called_once_with(fz, 38, len(bytes_), False, method)
|
|
1545
|
+
if repacker._scan_data_descriptor_no_sig_by_decompression(fz, 38, len(bytes_), False, method):
|
|
1546
|
+
m_sddns.assert_not_called()
|
|
1547
|
+
else:
|
|
1548
|
+
m_sddns.assert_called_once_with(fz, 38, len(bytes_), False)
|
|
1549
|
+
|
|
1550
|
+
# return None for data descriptor (unsigned) if `strict_descriptor=True`
|
|
1551
|
+
repacker = zipfile._ZipRepacker(strict_descriptor=True)
|
|
1552
|
+
bytes_ = self._generate_local_file_entry(
|
|
1553
|
+
'file.txt', b'dummy', compression=method, dd=True, dd_sig=False)
|
|
1554
|
+
fz = io.BytesIO(bytes_)
|
|
1555
|
+
with mock.patch.object(repacker, '_scan_data_descriptor',
|
|
1556
|
+
wraps=repacker._scan_data_descriptor) as m_sdd, \
|
|
1557
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig_by_decompression',
|
|
1558
|
+
wraps=repacker._scan_data_descriptor_no_sig_by_decompression) as m_sddnsbd, \
|
|
1559
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig',
|
|
1560
|
+
wraps=repacker._scan_data_descriptor_no_sig) as m_sddns:
|
|
1561
|
+
result = repacker._validate_local_file_entry(fz, 0, len(bytes_))
|
|
1562
|
+
self.assertEqual(result, None)
|
|
1563
|
+
m_sdd.assert_called_once_with(fz, 38, len(bytes_), False)
|
|
1564
|
+
m_sddnsbd.assert_not_called()
|
|
1565
|
+
m_sddns.assert_not_called()
|
|
1566
|
+
|
|
1567
|
+
@requires_zip64fix()
|
|
1568
|
+
def test_validate_local_file_entry_zip64_stored(self):
|
|
1569
|
+
self._test_validate_local_file_entry_zip64(method=zipfile.ZIP_STORED)
|
|
1570
|
+
|
|
1571
|
+
@requires_zip64fix()
|
|
1572
|
+
@requires_zlib()
|
|
1573
|
+
def test_validate_local_file_entry_zip64_zlib(self):
|
|
1574
|
+
self._test_validate_local_file_entry_zip64(method=zipfile.ZIP_DEFLATED)
|
|
1575
|
+
|
|
1576
|
+
@requires_zip64fix()
|
|
1577
|
+
@requires_bz2()
|
|
1578
|
+
def test_validate_local_file_entry_zip64_bz2(self):
|
|
1579
|
+
self._test_validate_local_file_entry_zip64(method=zipfile.ZIP_BZIP2)
|
|
1580
|
+
|
|
1581
|
+
@requires_zip64fix()
|
|
1582
|
+
@requires_lzma()
|
|
1583
|
+
def test_validate_local_file_entry_zip64_lzma(self):
|
|
1584
|
+
self._test_validate_local_file_entry_zip64(method=zipfile.ZIP_LZMA)
|
|
1585
|
+
|
|
1586
|
+
@requires_zip64fix()
|
|
1587
|
+
@requires_zstd()
|
|
1588
|
+
def test_validate_local_file_entry_zip64_zstd(self):
|
|
1589
|
+
self._test_validate_local_file_entry_zip64(method=zipfile.ZIP_ZSTANDARD)
|
|
1590
|
+
|
|
1591
|
+
def _test_validate_local_file_entry_zip64(self, method):
|
|
1592
|
+
repacker = zipfile._ZipRepacker()
|
|
1593
|
+
|
|
1594
|
+
# zip64
|
|
1595
|
+
bytes_ = self._generate_local_file_entry(
|
|
1596
|
+
'file.txt', b'dummy', compression=method, force_zip64=True)
|
|
1597
|
+
fz = io.BytesIO(bytes_)
|
|
1598
|
+
with mock.patch.object(repacker, '_scan_data_descriptor',
|
|
1599
|
+
wraps=repacker._scan_data_descriptor) as m_sdd, \
|
|
1600
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig_by_decompression',
|
|
1601
|
+
wraps=repacker._scan_data_descriptor_no_sig_by_decompression) as m_sddnsbd, \
|
|
1602
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig',
|
|
1603
|
+
wraps=repacker._scan_data_descriptor_no_sig) as m_sddns:
|
|
1604
|
+
result = repacker._validate_local_file_entry(fz, 0, len(bytes_))
|
|
1605
|
+
self.assertEqual(result, len(bytes_))
|
|
1606
|
+
m_sdd.assert_not_called()
|
|
1607
|
+
m_sddnsbd.assert_not_called()
|
|
1608
|
+
m_sddns.assert_not_called()
|
|
1609
|
+
|
|
1610
|
+
# data descriptor + zip64
|
|
1611
|
+
bytes_ = self._generate_local_file_entry(
|
|
1612
|
+
'file.txt', b'dummy', compression=method, force_zip64=True, dd=True)
|
|
1613
|
+
fz = io.BytesIO(bytes_)
|
|
1614
|
+
with mock.patch.object(repacker, '_scan_data_descriptor',
|
|
1615
|
+
wraps=repacker._scan_data_descriptor) as m_sdd, \
|
|
1616
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig_by_decompression',
|
|
1617
|
+
wraps=repacker._scan_data_descriptor_no_sig_by_decompression) as m_sddnsbd, \
|
|
1618
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig',
|
|
1619
|
+
wraps=repacker._scan_data_descriptor_no_sig) as m_sddns:
|
|
1620
|
+
result = repacker._validate_local_file_entry(fz, 0, len(bytes_))
|
|
1621
|
+
self.assertEqual(result, len(bytes_))
|
|
1622
|
+
m_sdd.assert_called_once_with(fz, 58, len(bytes_), True)
|
|
1623
|
+
m_sddnsbd.assert_not_called()
|
|
1624
|
+
m_sddns.assert_not_called()
|
|
1625
|
+
|
|
1626
|
+
# data descriptor (unsigned) + zip64
|
|
1627
|
+
bytes_ = self._generate_local_file_entry(
|
|
1628
|
+
'file.txt', b'dummy', compression=method, force_zip64=True, dd=True, dd_sig=False)
|
|
1629
|
+
fz = io.BytesIO(bytes_)
|
|
1630
|
+
with mock.patch.object(repacker, '_scan_data_descriptor',
|
|
1631
|
+
wraps=repacker._scan_data_descriptor) as m_sdd, \
|
|
1632
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig_by_decompression',
|
|
1633
|
+
wraps=repacker._scan_data_descriptor_no_sig_by_decompression) as m_sddnsbd, \
|
|
1634
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig',
|
|
1635
|
+
wraps=repacker._scan_data_descriptor_no_sig) as m_sddns:
|
|
1636
|
+
result = repacker._validate_local_file_entry(fz, 0, len(bytes_))
|
|
1637
|
+
self.assertEqual(result, len(bytes_))
|
|
1638
|
+
m_sdd.assert_called_once_with(fz, 58, len(bytes_), True)
|
|
1639
|
+
m_sddnsbd.assert_called_once_with(fz, 58, len(bytes_), True, method)
|
|
1640
|
+
if repacker._scan_data_descriptor_no_sig_by_decompression(fz, 58, len(bytes_), True, method):
|
|
1641
|
+
m_sddns.assert_not_called()
|
|
1642
|
+
else:
|
|
1643
|
+
m_sddns.assert_called_once_with(fz, 58, len(bytes_), True)
|
|
1644
|
+
|
|
1645
|
+
# return None for data descriptor (unsigned) if `strict_descriptor=True`
|
|
1646
|
+
repacker = zipfile._ZipRepacker(strict_descriptor=True)
|
|
1647
|
+
bytes_ = self._generate_local_file_entry(
|
|
1648
|
+
'file.txt', b'dummy', compression=method, force_zip64=True, dd=True, dd_sig=False)
|
|
1649
|
+
fz = io.BytesIO(bytes_)
|
|
1650
|
+
with mock.patch.object(repacker, '_scan_data_descriptor',
|
|
1651
|
+
wraps=repacker._scan_data_descriptor) as m_sdd, \
|
|
1652
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig_by_decompression',
|
|
1653
|
+
wraps=repacker._scan_data_descriptor_no_sig_by_decompression) as m_sddnsbd, \
|
|
1654
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig',
|
|
1655
|
+
wraps=repacker._scan_data_descriptor_no_sig) as m_sddns:
|
|
1656
|
+
result = repacker._validate_local_file_entry(fz, 0, len(bytes_))
|
|
1657
|
+
self.assertEqual(result, None)
|
|
1658
|
+
m_sdd.assert_called_once_with(fz, 58, len(bytes_), True)
|
|
1659
|
+
m_sddnsbd.assert_not_called()
|
|
1660
|
+
m_sddns.assert_not_called()
|
|
1661
|
+
|
|
1662
|
+
def test_validate_local_file_entry_encrypted(self):
|
|
1663
|
+
repacker = zipfile._ZipRepacker()
|
|
1664
|
+
|
|
1665
|
+
bytes_ = (
|
|
1666
|
+
b'PK\x03\x04'
|
|
1667
|
+
b'\x14\x00'
|
|
1668
|
+
b'\x09\x00'
|
|
1669
|
+
b'\x08\x00'
|
|
1670
|
+
b'\xAB\x28'
|
|
1671
|
+
b'\xD2\x5A'
|
|
1672
|
+
b'\x00\x00\x00\x00'
|
|
1673
|
+
b'\x00\x00\x00\x00'
|
|
1674
|
+
b'\x00\x00\x00\x00'
|
|
1675
|
+
b'\x08\x00'
|
|
1676
|
+
b'\x00\x00'
|
|
1677
|
+
b'file.txt'
|
|
1678
|
+
b'\x97\xF1\x83\x34\x9D\xC4\x8C\xD3\xED\x79\x8C\xA2\xBB\x49\xFF\x1B\x89'
|
|
1679
|
+
b'\x3F\xF2\xF4\x4F'
|
|
1680
|
+
b'\x11\x00\x00\x00'
|
|
1681
|
+
b'\x05\x00\x00\x00'
|
|
1682
|
+
)
|
|
1683
|
+
fz = io.BytesIO(bytes_)
|
|
1684
|
+
with mock.patch.object(repacker, '_scan_data_descriptor',
|
|
1685
|
+
wraps=repacker._scan_data_descriptor) as m_sdd, \
|
|
1686
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig_by_decompression',
|
|
1687
|
+
wraps=repacker._scan_data_descriptor_no_sig_by_decompression) as m_sddnsbd, \
|
|
1688
|
+
mock.patch.object(repacker, '_scan_data_descriptor_no_sig',
|
|
1689
|
+
wraps=repacker._scan_data_descriptor_no_sig) as m_sddns:
|
|
1690
|
+
result = repacker._validate_local_file_entry(fz, 0, len(bytes_))
|
|
1691
|
+
self.assertEqual(result, len(bytes_))
|
|
1692
|
+
m_sdd.assert_called_once_with(fz, 38, len(bytes_), False)
|
|
1693
|
+
m_sddnsbd.assert_not_called()
|
|
1694
|
+
m_sddns.assert_called_once_with(fz, 38, len(bytes_), False)
|
|
1695
|
+
|
|
1578
1696
|
def test_iter_scan_signature(self):
|
|
1579
1697
|
bytes_ = b'sig__sig__sig__sig'
|
|
1580
1698
|
ln = len(bytes_)
|
|
@@ -1623,136 +1741,177 @@ class ZipRepackerTests(unittest.TestCase):
|
|
|
1623
1741
|
|
|
1624
1742
|
def test_scan_data_descriptor(self):
|
|
1625
1743
|
repacker = zipfile._ZipRepacker()
|
|
1626
|
-
|
|
1744
|
+
|
|
1745
|
+
sig = zipfile._DD_SIGNATURE
|
|
1746
|
+
raw_bytes = comp_bytes = b'dummy'
|
|
1747
|
+
raw_len = comp_len = len(raw_bytes)
|
|
1748
|
+
raw_crc = zipfile.crc32(raw_bytes)
|
|
1627
1749
|
|
|
1628
1750
|
# basic
|
|
1629
|
-
bytes_ =
|
|
1751
|
+
bytes_ = comp_bytes + struct.pack('<4L', sig, raw_crc, comp_len, raw_len)
|
|
1630
1752
|
self.assertEqual(
|
|
1631
1753
|
repacker._scan_data_descriptor(io.BytesIO(bytes_), 0, len(bytes_), False),
|
|
1632
|
-
(
|
|
1754
|
+
(raw_crc, comp_len, raw_len, 16),
|
|
1633
1755
|
)
|
|
1634
1756
|
|
|
1635
1757
|
# return None if no signature
|
|
1636
|
-
bytes_ =
|
|
1758
|
+
bytes_ = comp_bytes + struct.pack('<3L', raw_crc, comp_len, raw_len)
|
|
1637
1759
|
self.assertEqual(
|
|
1638
1760
|
repacker._scan_data_descriptor(io.BytesIO(bytes_), 0, len(bytes_), False),
|
|
1639
1761
|
None,
|
|
1640
1762
|
)
|
|
1641
1763
|
|
|
1642
|
-
# return None if not
|
|
1643
|
-
bytes_ = struct.pack('<
|
|
1764
|
+
# return None if compressed size not match
|
|
1765
|
+
bytes_ = comp_bytes + struct.pack('<4L', sig, raw_crc, comp_len + 1, raw_len)
|
|
1644
1766
|
self.assertEqual(
|
|
1645
1767
|
repacker._scan_data_descriptor(io.BytesIO(bytes_), 0, len(bytes_), False),
|
|
1646
1768
|
None,
|
|
1647
1769
|
)
|
|
1648
1770
|
|
|
1649
|
-
|
|
1650
|
-
|
|
1771
|
+
bytes_ = comp_bytes + struct.pack('<4L', sig, raw_crc, comp_len - 1, raw_len)
|
|
1772
|
+
self.assertEqual(
|
|
1773
|
+
repacker._scan_data_descriptor(io.BytesIO(bytes_), 0, len(bytes_), False),
|
|
1774
|
+
None,
|
|
1775
|
+
)
|
|
1776
|
+
|
|
1777
|
+
bytes_ = b'1' + comp_bytes + struct.pack('<4L', sig, raw_crc, comp_len, raw_len)
|
|
1778
|
+
self.assertEqual(
|
|
1779
|
+
repacker._scan_data_descriptor(io.BytesIO(bytes_), 0, len(bytes_), False),
|
|
1780
|
+
None,
|
|
1781
|
+
)
|
|
1782
|
+
|
|
1783
|
+
bytes_ = comp_bytes[1:] + struct.pack('<4L', sig, raw_crc, comp_len, raw_len)
|
|
1651
1784
|
self.assertEqual(
|
|
1652
1785
|
repacker._scan_data_descriptor(io.BytesIO(bytes_), 0, len(bytes_), False),
|
|
1653
1786
|
None,
|
|
1654
1787
|
)
|
|
1655
1788
|
|
|
1656
1789
|
# zip64
|
|
1657
|
-
bytes_ =
|
|
1790
|
+
bytes_ = comp_bytes + struct.pack('<2L2Q', sig, raw_crc, comp_len, raw_len)
|
|
1658
1791
|
self.assertEqual(
|
|
1659
1792
|
repacker._scan_data_descriptor(io.BytesIO(bytes_), 0, len(bytes_), True),
|
|
1660
|
-
(
|
|
1793
|
+
(raw_crc, comp_len, raw_len, 24),
|
|
1661
1794
|
)
|
|
1662
1795
|
|
|
1663
1796
|
# offset
|
|
1664
|
-
bytes_ =
|
|
1797
|
+
bytes_ = comp_bytes + struct.pack('<4L', sig, raw_crc, comp_len, raw_len)
|
|
1665
1798
|
self.assertEqual(
|
|
1666
1799
|
repacker._scan_data_descriptor(io.BytesIO(bytes_), 1, len(bytes_), False),
|
|
1667
1800
|
None,
|
|
1668
1801
|
)
|
|
1669
1802
|
|
|
1670
|
-
bytes_ = b'
|
|
1803
|
+
bytes_ = b'123' + comp_bytes + struct.pack('<4L', sig, raw_crc, comp_len, raw_len)
|
|
1671
1804
|
self.assertEqual(
|
|
1672
1805
|
repacker._scan_data_descriptor(io.BytesIO(bytes_), 0, len(bytes_), False),
|
|
1673
1806
|
None,
|
|
1674
1807
|
)
|
|
1675
1808
|
self.assertEqual(
|
|
1676
1809
|
repacker._scan_data_descriptor(io.BytesIO(bytes_), 3, len(bytes_), False),
|
|
1677
|
-
(
|
|
1810
|
+
(raw_crc, comp_len, raw_len, 16),
|
|
1678
1811
|
)
|
|
1679
1812
|
|
|
1680
1813
|
# end_offset
|
|
1681
|
-
bytes_ =
|
|
1814
|
+
bytes_ = comp_bytes + struct.pack('<4L', sig, raw_crc, comp_len, raw_len)
|
|
1682
1815
|
self.assertEqual(
|
|
1683
1816
|
repacker._scan_data_descriptor(io.BytesIO(bytes_), 0, len(bytes_) - 1, False),
|
|
1684
1817
|
None,
|
|
1685
1818
|
)
|
|
1686
1819
|
|
|
1687
|
-
bytes_ =
|
|
1820
|
+
bytes_ = comp_bytes + struct.pack('<4L', sig, raw_crc, comp_len, raw_len) + b'123'
|
|
1688
1821
|
self.assertEqual(
|
|
1689
1822
|
repacker._scan_data_descriptor(io.BytesIO(bytes_), 0, len(bytes_) - 3, False),
|
|
1690
|
-
(
|
|
1823
|
+
(raw_crc, comp_len, raw_len, 16),
|
|
1824
|
+
)
|
|
1825
|
+
self.assertEqual(
|
|
1826
|
+
repacker._scan_data_descriptor(io.BytesIO(bytes_), 0, len(bytes_), False),
|
|
1827
|
+
(raw_crc, comp_len, raw_len, 16),
|
|
1691
1828
|
)
|
|
1692
1829
|
|
|
1693
1830
|
def test_scan_data_descriptor_no_sig(self):
|
|
1694
1831
|
repacker = zipfile._ZipRepacker()
|
|
1695
1832
|
|
|
1833
|
+
raw_bytes = comp_bytes = b'dummy'
|
|
1834
|
+
raw_len = comp_len = len(raw_bytes)
|
|
1835
|
+
raw_crc = zipfile.crc32(raw_bytes)
|
|
1836
|
+
|
|
1696
1837
|
# basic
|
|
1697
|
-
bytes_ =
|
|
1838
|
+
bytes_ = comp_bytes + struct.pack('<3L', raw_crc, comp_len, raw_len)
|
|
1698
1839
|
self.assertEqual(
|
|
1699
1840
|
repacker._scan_data_descriptor_no_sig(io.BytesIO(bytes_), 0, len(bytes_), False),
|
|
1700
|
-
(
|
|
1841
|
+
(raw_crc, comp_len, raw_len, 12),
|
|
1701
1842
|
)
|
|
1702
1843
|
|
|
1703
1844
|
# return None if compressed size not match
|
|
1704
|
-
bytes_ =
|
|
1845
|
+
bytes_ = comp_bytes + struct.pack('<3L', raw_crc, comp_len + 1, raw_len)
|
|
1705
1846
|
self.assertEqual(
|
|
1706
1847
|
repacker._scan_data_descriptor_no_sig(io.BytesIO(bytes_), 0, len(bytes_), False),
|
|
1707
1848
|
None,
|
|
1708
1849
|
)
|
|
1709
1850
|
|
|
1710
|
-
|
|
1711
|
-
bytes_ = b'dummy' + struct.pack('<L2Q', 0x4ff4f23f, 5, 5)
|
|
1851
|
+
bytes_ = comp_bytes + struct.pack('<3L', raw_crc, comp_len - 1, raw_len)
|
|
1712
1852
|
self.assertEqual(
|
|
1713
|
-
repacker._scan_data_descriptor_no_sig(io.BytesIO(bytes_), 0, len(bytes_),
|
|
1714
|
-
|
|
1853
|
+
repacker._scan_data_descriptor_no_sig(io.BytesIO(bytes_), 0, len(bytes_), False),
|
|
1854
|
+
None,
|
|
1715
1855
|
)
|
|
1716
1856
|
|
|
1717
|
-
|
|
1718
|
-
bytes_ = b'dummy' + struct.pack('<3L', 0x4ff4f23f, 5, 5)
|
|
1857
|
+
bytes_ = b'1' + comp_bytes + struct.pack('<3L', raw_crc, comp_len, raw_len)
|
|
1719
1858
|
self.assertEqual(
|
|
1720
|
-
repacker._scan_data_descriptor_no_sig(io.BytesIO(bytes_),
|
|
1859
|
+
repacker._scan_data_descriptor_no_sig(io.BytesIO(bytes_), 0, len(bytes_), False),
|
|
1721
1860
|
None,
|
|
1722
1861
|
)
|
|
1723
1862
|
|
|
1724
|
-
bytes_ =
|
|
1863
|
+
bytes_ = comp_bytes[1:] + struct.pack('<3L', raw_crc, comp_len, raw_len)
|
|
1725
1864
|
self.assertEqual(
|
|
1726
1865
|
repacker._scan_data_descriptor_no_sig(io.BytesIO(bytes_), 0, len(bytes_), False),
|
|
1727
1866
|
None,
|
|
1728
1867
|
)
|
|
1868
|
+
|
|
1869
|
+
# zip64
|
|
1870
|
+
bytes_ = comp_bytes + struct.pack('<L2Q', raw_crc, comp_len, raw_len)
|
|
1871
|
+
self.assertEqual(
|
|
1872
|
+
repacker._scan_data_descriptor_no_sig(io.BytesIO(bytes_), 0, len(bytes_), True),
|
|
1873
|
+
(raw_crc, comp_len, raw_len, 20),
|
|
1874
|
+
)
|
|
1875
|
+
|
|
1876
|
+
# offset
|
|
1877
|
+
bytes_ = comp_bytes + struct.pack('<3L', raw_crc, comp_len, raw_len)
|
|
1878
|
+
self.assertEqual(
|
|
1879
|
+
repacker._scan_data_descriptor_no_sig(io.BytesIO(bytes_), 1, len(bytes_), False),
|
|
1880
|
+
None,
|
|
1881
|
+
)
|
|
1882
|
+
|
|
1883
|
+
bytes_ = b'123' + comp_bytes + struct.pack('<3L', raw_crc, comp_len, raw_len)
|
|
1729
1884
|
self.assertEqual(
|
|
1730
1885
|
repacker._scan_data_descriptor_no_sig(io.BytesIO(bytes_), 3, len(bytes_), False),
|
|
1731
|
-
(
|
|
1886
|
+
(raw_crc, comp_len, raw_len, 12),
|
|
1732
1887
|
)
|
|
1733
1888
|
|
|
1734
1889
|
# end_offset
|
|
1735
|
-
bytes_ =
|
|
1890
|
+
bytes_ = comp_bytes + struct.pack('<3L', raw_crc, comp_len, raw_len)
|
|
1736
1891
|
self.assertEqual(
|
|
1737
1892
|
repacker._scan_data_descriptor_no_sig(io.BytesIO(bytes_), 0, len(bytes_) - 1, False),
|
|
1738
1893
|
None,
|
|
1739
1894
|
)
|
|
1740
1895
|
|
|
1741
|
-
bytes_ =
|
|
1896
|
+
bytes_ = comp_bytes + struct.pack('<3L', raw_crc, comp_len, raw_len) + b'123'
|
|
1742
1897
|
self.assertEqual(
|
|
1743
1898
|
repacker._scan_data_descriptor_no_sig(io.BytesIO(bytes_), 0, len(bytes_) - 3, False),
|
|
1744
|
-
(
|
|
1899
|
+
(raw_crc, comp_len, raw_len, 12),
|
|
1900
|
+
)
|
|
1901
|
+
self.assertEqual(
|
|
1902
|
+
repacker._scan_data_descriptor_no_sig(io.BytesIO(bytes_), 0, len(bytes_), False),
|
|
1903
|
+
(raw_crc, comp_len, raw_len, 12),
|
|
1745
1904
|
)
|
|
1746
1905
|
|
|
1747
1906
|
# chunk_size
|
|
1748
|
-
bytes_ =
|
|
1907
|
+
bytes_ = comp_bytes + struct.pack('<3L', raw_crc, comp_len, raw_len)
|
|
1749
1908
|
self.assertEqual(
|
|
1750
1909
|
repacker._scan_data_descriptor_no_sig(io.BytesIO(bytes_), 0, len(bytes_), False, 12),
|
|
1751
|
-
(
|
|
1910
|
+
(raw_crc, comp_len, raw_len, 12),
|
|
1752
1911
|
)
|
|
1753
1912
|
self.assertEqual(
|
|
1754
1913
|
repacker._scan_data_descriptor_no_sig(io.BytesIO(bytes_), 0, len(bytes_), False, 1),
|
|
1755
|
-
(
|
|
1914
|
+
(raw_crc, comp_len, raw_len, 12),
|
|
1756
1915
|
)
|
|
1757
1916
|
|
|
1758
1917
|
def test_scan_data_descriptor_no_sig_by_decompression_stored(self):
|
|
@@ -1781,44 +1940,40 @@ class ZipRepackerTests(unittest.TestCase):
|
|
|
1781
1940
|
def _test_scan_data_descriptor_no_sig_by_decompression(self, method):
|
|
1782
1941
|
repacker = zipfile._ZipRepacker()
|
|
1783
1942
|
|
|
1784
|
-
compressor = zipfile._get_compressor(method)
|
|
1785
|
-
|
|
1786
1943
|
raw_bytes = b'dummy'
|
|
1787
1944
|
raw_len = len(raw_bytes)
|
|
1945
|
+
raw_crc = zipfile.crc32(raw_bytes)
|
|
1946
|
+
|
|
1947
|
+
compressor = zipfile._get_compressor(method)
|
|
1788
1948
|
comp_bytes = compressor.compress(raw_bytes)
|
|
1789
1949
|
comp_bytes += compressor.flush()
|
|
1790
1950
|
comp_len = len(comp_bytes)
|
|
1791
1951
|
|
|
1792
1952
|
# basic
|
|
1793
|
-
bytes_ = comp_bytes + struct.pack('<3L',
|
|
1953
|
+
bytes_ = comp_bytes + struct.pack('<3L', raw_crc, comp_len, raw_len)
|
|
1794
1954
|
self.assertEqual(
|
|
1795
1955
|
repacker._scan_data_descriptor_no_sig_by_decompression(
|
|
1796
1956
|
io.BytesIO(bytes_), 0, len(bytes_), False, method),
|
|
1797
|
-
(
|
|
1957
|
+
(raw_crc, comp_len, raw_len, 12),
|
|
1798
1958
|
)
|
|
1799
1959
|
|
|
1800
|
-
# return None if
|
|
1801
|
-
bytes_ = comp_bytes + struct.pack('<3L',
|
|
1960
|
+
# return None if data length < DD signature
|
|
1961
|
+
bytes_ = comp_bytes + struct.pack('<3L', raw_crc, comp_len, raw_len)
|
|
1802
1962
|
self.assertEqual(
|
|
1803
1963
|
repacker._scan_data_descriptor_no_sig_by_decompression(
|
|
1804
|
-
io.BytesIO(bytes_), 0,
|
|
1964
|
+
io.BytesIO(bytes_), 0, 11, False, method),
|
|
1805
1965
|
None,
|
|
1806
1966
|
)
|
|
1807
1967
|
|
|
1808
|
-
|
|
1968
|
+
# return None if compressed size not match
|
|
1969
|
+
bytes_ = comp_bytes + struct.pack('<3L', raw_crc, comp_len + 1, raw_len)
|
|
1809
1970
|
self.assertEqual(
|
|
1810
1971
|
repacker._scan_data_descriptor_no_sig_by_decompression(
|
|
1811
1972
|
io.BytesIO(bytes_), 0, len(bytes_), False, method),
|
|
1812
1973
|
None,
|
|
1813
1974
|
)
|
|
1814
|
-
self.assertEqual(
|
|
1815
|
-
repacker._scan_data_descriptor_no_sig_by_decompression(
|
|
1816
|
-
io.BytesIO(bytes_), 0, len(bytes_) + 1, False, method),
|
|
1817
|
-
None,
|
|
1818
|
-
)
|
|
1819
1975
|
|
|
1820
|
-
|
|
1821
|
-
bytes_ = comp_bytes + struct.pack('<3L', 0x4ff4f23f, comp_len - 1, raw_len)
|
|
1976
|
+
bytes_ = comp_bytes + struct.pack('<3L', raw_crc, comp_len - 1, raw_len)
|
|
1822
1977
|
self.assertEqual(
|
|
1823
1978
|
repacker._scan_data_descriptor_no_sig_by_decompression(
|
|
1824
1979
|
io.BytesIO(bytes_), 0, len(bytes_), False, method),
|
|
@@ -1826,41 +1981,46 @@ class ZipRepackerTests(unittest.TestCase):
|
|
|
1826
1981
|
)
|
|
1827
1982
|
|
|
1828
1983
|
# zip64
|
|
1829
|
-
bytes_ = comp_bytes + struct.pack('<L2Q',
|
|
1984
|
+
bytes_ = comp_bytes + struct.pack('<L2Q', raw_crc, comp_len, raw_len)
|
|
1830
1985
|
self.assertEqual(
|
|
1831
1986
|
repacker._scan_data_descriptor_no_sig_by_decompression(
|
|
1832
1987
|
io.BytesIO(bytes_), 0, len(bytes_), True, method),
|
|
1833
|
-
(
|
|
1988
|
+
(raw_crc, comp_len, raw_len, 20),
|
|
1834
1989
|
)
|
|
1835
1990
|
|
|
1836
1991
|
# offset
|
|
1837
|
-
bytes_ = comp_bytes + struct.pack('<3L',
|
|
1992
|
+
bytes_ = comp_bytes + struct.pack('<3L', raw_crc, comp_len, raw_len)
|
|
1838
1993
|
self.assertEqual(
|
|
1839
1994
|
repacker._scan_data_descriptor_no_sig_by_decompression(
|
|
1840
1995
|
io.BytesIO(bytes_), 1, len(bytes_), False, method),
|
|
1841
1996
|
None,
|
|
1842
1997
|
)
|
|
1843
1998
|
|
|
1844
|
-
bytes_ = b'123' + comp_bytes + struct.pack('<3L',
|
|
1999
|
+
bytes_ = b'123' + comp_bytes + struct.pack('<3L', raw_crc, comp_len, raw_len)
|
|
1845
2000
|
self.assertEqual(
|
|
1846
2001
|
repacker._scan_data_descriptor_no_sig_by_decompression(
|
|
1847
2002
|
io.BytesIO(bytes_), 3, len(bytes_), False, method),
|
|
1848
|
-
(
|
|
2003
|
+
(raw_crc, comp_len, raw_len, 12),
|
|
1849
2004
|
)
|
|
1850
2005
|
|
|
1851
2006
|
# end_offset
|
|
1852
|
-
bytes_ = comp_bytes + struct.pack('<3L',
|
|
2007
|
+
bytes_ = comp_bytes + struct.pack('<3L', raw_crc, comp_len, raw_len)
|
|
1853
2008
|
self.assertEqual(
|
|
1854
2009
|
repacker._scan_data_descriptor_no_sig_by_decompression(
|
|
1855
|
-
io.BytesIO(bytes_), 0, len(bytes_) -
|
|
2010
|
+
io.BytesIO(bytes_), 0, len(bytes_) - 1, False, method),
|
|
1856
2011
|
None,
|
|
1857
2012
|
)
|
|
1858
2013
|
|
|
1859
|
-
bytes_ = comp_bytes + struct.pack('<3L',
|
|
2014
|
+
bytes_ = comp_bytes + struct.pack('<3L', raw_crc, comp_len, raw_len) + b'123'
|
|
1860
2015
|
self.assertEqual(
|
|
1861
2016
|
repacker._scan_data_descriptor_no_sig_by_decompression(
|
|
1862
|
-
io.BytesIO(bytes_), 0, len(bytes_) -
|
|
1863
|
-
(
|
|
2017
|
+
io.BytesIO(bytes_), 0, len(bytes_) - 3, False, method),
|
|
2018
|
+
(raw_crc, comp_len, raw_len, 12),
|
|
2019
|
+
)
|
|
2020
|
+
self.assertEqual(
|
|
2021
|
+
repacker._scan_data_descriptor_no_sig_by_decompression(
|
|
2022
|
+
io.BytesIO(bytes_), 0, len(bytes_), False, method),
|
|
2023
|
+
(raw_crc, comp_len, raw_len, 12),
|
|
1864
2024
|
)
|
|
1865
2025
|
|
|
1866
2026
|
def _test_scan_data_descriptor_no_sig_by_decompression_invalid(self, method):
|
|
@@ -18,16 +18,19 @@ except ImportError:
|
|
|
18
18
|
# polyfill for Python < 3.12
|
|
19
19
|
from test.test_zipfile import Unseekable, requires_zlib
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
def requires_resource(res):
|
|
22
|
+
if not hasattr(requires_resource, '_resources'):
|
|
23
|
+
requires_resource._resources = set(os.environ.get("TEST_RESOURCES", "").split(","))
|
|
24
24
|
return unittest.skipUnless(
|
|
25
|
-
|
|
26
|
-
f"requires resource
|
|
25
|
+
res in requires_resource._resources,
|
|
26
|
+
f"requires resource {res!r} in envvar TEST_RESOURCES"
|
|
27
27
|
)
|
|
28
28
|
|
|
29
|
+
@requires_resource('extralargefile')
|
|
30
|
+
def setUpModule():
|
|
31
|
+
pass
|
|
32
|
+
|
|
29
33
|
|
|
30
|
-
@requires('extralargefile')
|
|
31
34
|
class TestRepack(unittest.TestCase):
|
|
32
35
|
def setUp(self):
|
|
33
36
|
# Create test data.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|