Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Lib/test/test_zipfile/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -4827,6 +4827,29 @@ def test_overlap_with_archive_comment(self):
with self.assertRaisesRegex(zipfile.BadZipFile, 'Overlapped entries'):
zipf.read('a')

def test_append_keep_filename(self):
"""Files loaded from an archive should keep original filename when
rewritten to central directory in append mode."""
with mock.patch('os.sep', '/'), mock.patch('os.altsep', None):
with zipfile.ZipFile(TESTFN, mode="w") as zipfp:
zinfo = zipfile.ZipInfo('MyFolder/My\\File.txt')
zipfp.writestr(zinfo, 'foo')

with mock.patch('os.sep', '\\'), mock.patch('os.altsep', '/'):
with zipfile.ZipFile(TESTFN, "a") as zipfp:
zi = zipfp.infolist()[0]
self.assertEqual(zi.orig_filename, 'MyFolder/My\\File.txt')
self.assertEqual(zi.filename, 'MyFolder/My/File.txt')
self.assertIsNone(zipfp.testzip())
# trigger archive rewriting
zipfp.comment = b''

with zipfile.ZipFile(TESTFN, "r") as zipfp:
zi = zipfp.infolist()[0]
self.assertEqual(zi.orig_filename, 'MyFolder/My\\File.txt')
self.assertEqual(zi.filename, 'MyFolder/My/File.txt')
self.assertIsNone(zipfp.testzip())

def tearDown(self):
unlink(TESTFN)
unlink(TESTFN2)
Expand Down
23 changes: 18 additions & 5 deletions Lib/zipfile/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ class ZipInfo:
"""Class with attributes describing each file in the ZIP archive."""

__slots__ = (
'orig_filename',
'_orig_filename',
'filename',
'date_time',
'compress_type',
Expand All @@ -456,8 +456,6 @@ class ZipInfo:
)

def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
self.orig_filename = filename # Original file name in archive

# Terminate the file name at the first null byte and
# ensure paths always use forward slashes as the directory separator.
filename = _sanitize_filename(filename)
Expand Down Expand Up @@ -487,11 +485,25 @@ def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
self.external_attr = 0 # External file attributes
self.compress_size = 0 # Size of the compressed file
self.file_size = 0 # Size of the uncompressed file

# Special internal attributes set by class ZipFile when read from an archive:
self._orig_filename = None # Original file name in archive
self._end_offset = None # Start of the next local header or central directory

# Other attributes are set by class ZipFile:
# header_offset Byte offset to the file header
# CRC CRC-32 of the uncompressed file

@property
def orig_filename(self):
return (self._orig_filename if self._orig_filename is not None
else self.filename)

# Maintain backward compatibility in case someone sets it.
@orig_filename.setter
def orig_filename(self, value):
self._orig_filename = value

# Maintain backward compatibility with the old protected attribute name.
@property
def _compresslevel(self):
Expand Down Expand Up @@ -579,9 +591,9 @@ def _encodeFilenameFlags(self):
else:
encoding = 'cp437'
try:
return self.filename.encode(encoding), self.flag_bits & ~_MASK_UTF_FILENAME
return self.orig_filename.encode(encoding), self.flag_bits & ~_MASK_UTF_FILENAME
except UnicodeEncodeError:
return self.filename.encode('utf-8'), self.flag_bits | _MASK_UTF_FILENAME
return self.orig_filename.encode('utf-8'), self.flag_bits | _MASK_UTF_FILENAME

def _decodeExtra(self, filename_crc):
# Try to decode the extra field.
Expand Down Expand Up @@ -2056,6 +2068,7 @@ def _RealGetContents(self):
filename = filename.decode(self.metadata_encoding or 'cp437')
# Create ZipInfo instance to store file information
x = ZipInfo(filename)
x._orig_filename = filename
x.extra = fp.read(centdir[_CD_EXTRA_FIELD_LENGTH])
x.comment = fp.read(centdir[_CD_COMMENT_LENGTH])
x.header_offset = centdir[_CD_LOCAL_HEADER_OFFSET]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix an issue that an auto-sanitized filename corrupts a ZIP archive when rewritten to the central directory in append mode.
Loading