[3.8] gh-123270: Replaced SanitizedNames with a more surgical fix. (GH-123354) by jaraco · Pull Request #123433 · python/cpython

Expand Up @@ -3007,6 +3007,83 @@ def test_implied_dirs_performance(self): data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)] zipfile.CompleteDirs._implied_dirs(data)
def test_malformed_paths(self): """ Path should handle malformed paths gracefully.
Paths with leading slashes are not visible.
Paths with dots are treated like regular files. """ data = io.BytesIO() zf = zipfile.ZipFile(data, "w") zf.writestr("/one-slash.txt", b"content") zf.writestr("//two-slash.txt", b"content") zf.writestr("../parent.txt", b"content") zf.filename = '' root = zipfile.Path(zf) assert list(map(str, root.iterdir())) == ['../'] assert root.joinpath('..').joinpath('parent.txt').read_bytes() == b'content'
def test_unsupported_names(self): """ Path segments with special characters are readable.
On some platforms or file systems, characters like ``:`` and ``?`` are not allowed, but they are valid in the zip file. """ data = io.BytesIO() zf = zipfile.ZipFile(data, "w") zf.writestr("path?", b"content") zf.writestr("V: NMS.flac", b"fLaC...") zf.filename = '' root = zipfile.Path(zf) contents = root.iterdir() assert next(contents).name == 'path?' assert next(contents).name == 'V: NMS.flac' assert root.joinpath('V: NMS.flac').read_bytes() == b"fLaC..."
def test_backslash_not_separator(self): """ In a zip file, backslashes are not separators. """ data = io.BytesIO() zf = zipfile.ZipFile(data, "w") zf.writestr(DirtyZipInfo.for_name("foo\\bar", zf), b"content") zf.filename = '' root = zipfile.Path(zf) (first,) = root.iterdir() assert not first.is_dir() assert first.name == 'foo\\bar'

class DirtyZipInfo(zipfile.ZipInfo): """ Bypass name sanitization. """
def __init__(self, filename, *args, **kwargs): super().__init__(filename, *args, **kwargs) self.filename = filename
@classmethod def for_name(cls, name, archive): """ Construct the same way that ZipFile.writestr does.
TODO: extract this functionality and re-use """ self = cls(filename=name, date_time=time.localtime(time.time())[:6]) self.compress_type = archive.compression self.compress_level = archive.compresslevel if self.filename.endswith('/'): # pragma: no cover self.external_attr = 0o40775 << 16 # drwxrwxr-x self.external_attr |= 0x10 # MS-DOS directory flag else: self.external_attr = 0o600 << 16 # ?rw------- return self

if __name__ == "__main__": unittest.main()