8000 [3.13] gh-117779: Fix reading duplicated entries in zipfile by name (… · python/cpython@67c7de4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 67c7de4

Browse files
[3.13] gh-117779: Fix reading duplicated entries in zipfile by name (GH-129254) (GH-132263)
(cherry picked from commit 0f04f24) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 5911768 commit 67c7de4

File tree

3 files changed

+121
-6
lines changed

3 files changed

+121
-6
lines changed

Lib/test/test_zipfile/test_core.py

+106-2
< 67F4 td data-grid-cell-id="diff-3f7f7f9e251efe3ca7872e540c7840c85d508fc11150926df502ced996b98e9f-2380-2419-0" data-selected="false" role="gridcell" style="background-color:var(--diffBlob-additionNum-bgColor, var(--diffBlob-addition-bgColor-num));text-align:center" tabindex="-1" valign="top" class="focusable-grid-cell diff-line-number position-relative left-side">
Original file line numberDiff line numberDiff line change
@@ -2351,7 +2351,36 @@ def test_decompress_without_3rd_party_library(self):
23512351
self.assertRaises(RuntimeError, zf.extract, 'a.txt')
23522352

23532353
@requires_zlib()
2354-
def test_full_overlap(self):
2354+
def test_full_overlap_different_names(self):
2355+
data = (
2356+
b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e'
2357+
b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00b\xed'
2358+
b'\xc0\x81\x08\x00\x00\x00\xc00\xd6\xfbK\\d\x0b`P'
2359+
b'K\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2'
2360+
b'\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00'
2361+
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00aPK'
2362+
b'\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e'
2363+
b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00\x00'
2364+
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00bPK\x05'
2365+
b'\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00\x00/\x00\x00'
2366+
b'\x00\x00\x00'
2367+
)
2368+
with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf:
2369+
self.assertEqual(zipf.namelist(), ['a', 'b'])
2370+
zi = zipf.getinfo('a')
2371+
self.assertEqual(zi.header_offset, 0)
2372+
self.assertEqual(zi.compress_size, 16)
2373+
self.assertEqual(zi.file_size, 1033)
2374+
zi = zipf.getinfo('b')
2375+
self.assertEqual(zi.header_offset, 0)
2376+
self.assertEqual(zi.compress_size, 16)
2377+
self.assertEqual(zi.file_size, 1033)
2378+
self.assertEqual(len(zipf.read('b')), 1033)
2379+
with self.assertRaisesRegex(zipfile.BadZipFile, 'File name.*differ'):
2380+
zipf.read('a')
2381+
2382+
@requires_zlib()
2383+
def test_full_overlap_different_names2(self):
23552384
data = (
23562385
b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e'
23572386
b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00a\xed'
@@ -2375,9 +2404,43 @@ def test_full_overlap(self):
23752404
self.assertEqual(zi.header_offset, 0)
23762405
self.assertEqual(zi.compress_size, 16)
23772406
self.assertEqual(zi.file_size, 1033)
2378-
self.assertEqual(len(zipf.read('a')), 1033)
23792407
with self.assertRaisesRegex(zipfile.BadZipFile, 'File name.*differ'):
23802408
zipf.read('b')
2409+
with self.assertWarnsRegex(UserWarning, 'Overlapped entries') as cm:
2410+
self.assertEqual(len(zipf.read('a')), 1033)
2411+
self.assertEqual(cm.filename, __file__)
2412+
2413+
@requires_zlib()
2414+
def test_full_overlap_same_name(self):
2415+
data = (
2416+
b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e'
2417+
b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00a\xed'
2418+
b'\xc0\x81\x08\x00\x00\x00\xc00\xd6\xfbK\\d\x0b`P'
2419+
b'K\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2'
2420+
b'\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00'
2421+
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00aPK'
2422+
b'\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e'
2423+
b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00\x00'
2424+
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00aPK\x05'
2425+
b'\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00\x00/\x00\x00'
2426+
b'\x00\x00\x00'
2427+
)
2428+
with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf:
2429+
self.assertEqual(zipf.namelist(), ['a', 'a'])
2430+
self.assertEqual(len(zipf.infolist()), 2)
2431+
zi = zipf.getinfo('a')
2432+
self.assertEqual(zi.header_offset, 0)
2433+
self.assertEqual(zi.compress_size, 16)
2434+
self.assertEqual(zi.file_size, 1033)
2435+
self.assertEqual(len(zipf.read('a')), 1033)
2436+
self.assertEqual(len(zipf.read(zi)), 1033)
2437+
self.assertEqual(len(zipf.read(zipf.infolist()[1])), 1033)
2438+
with self.assertWarnsRegex(UserWarning, 'Overlapped entries') as cm:
2439+
self.assertEqual(len(zipf.read(zipf.infolist()[0])), 1033)
2440+
self.assertEqual(cm.filename, __file__)
2441+
with self.assertWarnsRegex(UserWarning, 'Overlapped entries') as cm:
2442+
zipf.open(zipf.infolist()[0]).close()
2443+
self.assertEqual(cm.filename, __file__)
23812444

23822445
@requires_zlib()
23832446
def test_quoted_overlap(self):
@@ -2410,6 +2473,47 @@ def test_quoted_overlap(self):
24102473
zipf.read('a')
24112474
self.assertEqual(len(zipf.read('b')), 1033)
24122475

2476+
@requires_zlib()
2477+
def test_overlap_with_central_dir(self):
2478+
data = (
2479+
b'PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00G_|Z'
2480+
b'\xe2\x1e8\xbb\x0b\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00'
2481+
b'\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81\x00\x00\x00\x00aP'
2482+
b'K\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00/\x00\x00\x00\x00'
2483+
b'\x00\x00\x00\x00\x00'
2484+
)
2485+
with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf:
2486+
self.assertEqual(zipf.namelist(), ['a'])
2487+
self.assertEqual(len(zipf.infolist()), 1)
2488+
zi = zipf.getinfo('a')
2489+
self.assertEqual(zi.header_offset, 0)
2490+
self.assertEqual(zi.compress_size, 11)
2491+
self.assertEqual(zi.file_size, 1033)
2492+
with self.assertRaisesRegex(zipfile.BadZipFile, 'Bad magic number'):
2493+
zipf.read('a')
2494+
2495+
@requires_zlib()
2496+
def test_overlap_with_archive_comment(self):
2497+
data = (
2498+
b'PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00G_|Z'
2499+
b'\xe2\x1e8\xbb\x0b\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00'
2500+
b'\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81E\x00\x00\x00aP'
2501+
b'K\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00/\x00\x00\x00\x00'
2502+
b'\x00\x00\x00*\x00'
2503+
b'PK\x03\x04\x14\x00\x00\x00\x08\x00G_|Z\xe2\x1e'
2504+
b'8\xbb\x0b\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00aK'
2505+
b'L\x1c\x05\xa3`\x14\x8cx\x00\x00'
2506+
)
2507+
with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf:
2508+
self.assertEqual(zipf.namelist(), ['a'])
2509+
self.assertEqual(len(zipf.infolist()), 1)
2510+
zi = zipf.getinfo('a')
2511+
self.assertEqual(zi.header_offset, 69)
2512+
self.assertEqual(zi.compress_size, 11)
2513+
self.assertEqual(zi.file_size, 1033)
2514+
with self.assertRaisesRegex(zipfile.BadZipFile, 'Overlapped entries'):
2515+
zipf.read('a')
2516+
24132517
def tearDown(self):
24142518
unlink(TESTFN)
24152519
unlink(TESTFN2)

Lib/zipfile/__init__.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -1521,9 +1521,8 @@ def _RealGetContents(self):
15211521
print("total", total)
15221522

15231523
end_offset = self.start_dir
1524-
for zinfo in sorted(self.filelist,
1525-
key=lambda zinfo: zinfo.header_offset,
1526-
reverse=True):
1524+
for zinfo in reversed(sorted(self.filelist,
1525+
key=lambda zinfo: zinfo.header_offset)):
15271526
zinfo._end_offset = end_offset
15281527
end_offset = zinfo.header_offset
15291528

@@ -1685,7 +1684,16 @@ def open(self, name, mode="r", pwd=None, *, force_zip64=False):
16851684

16861685
if (zinfo._end_offset is not None and
16871686
zef_file.tell() + zinfo.compress_size > zinfo._end_offset):
1688-
raise BadZipFile(f"Overlapped entries: {zinfo.orig_filename!r} (possible zip bomb)")
1687+
if zinfo._end_offset == zinfo.header_offset:
1688+
import warnings
1689+
warnings.warn(
1690+
f"Overlapped entries: {zinfo.orig_filename!r} "
1691+
f"(possible zip bomb)",
1692+
skip_file_prefixes=(os.path.dirname(__file__),))
1693+
else:
1694+
raise BadZipFile(
1695+
f"Overlapped entries: {zinfo.orig_filename!r} "
1696+
f"(possible zip bomb)")
16891697

16901698
# check for encrypted flag & handle password
16911699
is_encrypted = zinfo.flag_bits & _MASK_ENCRYPTED
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix reading duplicated entries in :mod:`zipfile` by name.
2+
Reading duplicated entries (except the last one) by ``ZipInfo``
3+
now emits a warning instead of raising an exception.

0 commit comments

Comments
 (0)
0