diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index 2a8592f8bd69c1..24e3da61ebdcb1 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -715,7 +715,7 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. Never extract archives from untrusted sources without prior inspection. It is possible that files are created outside of the path specified in the *extract_dir* argument, e.g. members that have absolute filenames - starting with "/" or filenames with two dots "..". + starting with "/" or filenames with relative path components "../". Since Python 3.14, the defaults for both built-in formats (zip and tar files) will prevent the most dangerous of such security issues, diff --git a/Lib/shutil.py b/Lib/shutil.py index 171489ca41f2a7..3ae542724ca0b8 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -1245,27 +1245,10 @@ def _unpack_zipfile(filename, extract_dir): if not zipfile.is_zipfile(filename): raise ReadError("%s is not a zip file" % filename) - zip = zipfile.ZipFile(filename) - try: - for info in zip.infolist(): - name = info.filename - - # don't extract absolute paths or ones with .. in them - if name.startswith('/') or '..' in name: - continue - - targetpath = os.path.join(extract_dir, *name.split('/')) - if not targetpath: - continue - - _ensure_directory(targetpath) - if not name.endswith('/'): - # file - with zip.open(name, 'r') as source, \ - open(targetpath, 'wb') as target: - copyfileobj(source, target) - finally: - zip.close() + with zipfile.ZipFile(filename) as zf: + # delegate extracting the archive to zipfile + _ensure_directory(extract_dir) + zf.extractall(extract_dir) def _unpack_tarfile(filename, extract_dir, *, filter=None): """Unpack tar/tar.gz/tar.bz2/tar.xz `filename` to `extract_dir` diff --git a/Misc/NEWS.d/next/Library/2023-11-07-17-08-09.gh-issue-111791.oJbxOQ.rst b/Misc/NEWS.d/next/Library/2023-11-07-17-08-09.gh-issue-111791.oJbxOQ.rst new file mode 100644 index 00000000000000..eb0b18cfa77a4e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-07-17-08-09.gh-issue-111791.oJbxOQ.rst @@ -0,0 +1 @@ +ZIP archive entries containing two dots ".." in their name caused shutil's unpack_archive method to silently skip these files during extraction. This has been fixed, shutil's unpack_archive method now generates the same results as Python's own extractall method in the zipfile module, or the Linux unzip tool.