From 0050ba20cd013ce5186f1e4afc9175586f2d537d Mon Sep 17 00:00:00 2001 From: cwhite911 Date: Tue, 14 Jul 2026 10:05:42 -0400 Subject: [PATCH] r.unpack, v.unpack: Refuse to extract archives without a tarfile filter The pre-PEP 706 fallback extracted archives with a bare extractall(), which allows path traversal outside the target directory. The 'data' filter is available in Python 3.11.4, 3.12, and newer, so make its absence a fatal error instead of extracting unsafely after a warning. Apply the same to the identical fallbacks in g.extension and grass.utils.download. Fixes 2 Bandit B202 (tarfile_unsafe_members) alerts. --- python/grass/utils/download.py | 21 ++++++++------------- scripts/g.extension/g.extension.py | 19 ++++++------------- scripts/r.unpack/r.unpack.py | 18 ++++++------------ scripts/v.unpack/v.unpack.py | 18 ++++++------------ 4 files changed, 26 insertions(+), 50 deletions(-) diff --git a/python/grass/utils/download.py b/python/grass/utils/download.py index ae93d4efecc..6a83bb8a996 100644 --- a/python/grass/utils/download.py +++ b/python/grass/utils/download.py @@ -60,19 +60,14 @@ def extract_tar(name, directory, tmpdir): extract_dir = os.path.join(tmpdir, "extract_dir") Path(extract_dir).mkdir() - # Extraction filters were added in Python 3.12, - # and backported to 3.8.17, 3.9.17, 3.10.12, and 3.11.4 - # See - # https://docs.python.org/3.12/library/tarfile.html#tarfile-extraction-filter - # and https://peps.python.org/pep-0706/ - # In Python 3.12, using `filter=None` triggers a DepreciationWarning, - # and in Python 3.14, `filter='data'` will be the default - if hasattr(tarfile, "data_filter"): - tar.extractall(path=extract_dir, filter="data") - else: - # Remove this when no longer needed - debug(_("Extracting may be unsafe; consider updating Python")) - tar.extractall(path=extract_dir) + # The 'data' extraction filter was added in Python 3.12 and backported + # to 3.11.4 (PEP 706). Refuse to extract without it rather + # than extracting unsafely. + if not hasattr(tarfile, "data_filter"): + raise DownloadError( + _("Extracting may be unsafe; upgrade Python to 3.11.4 or newer") + ) + tar.extractall(path=extract_dir, filter="data") files = os.listdir(extract_dir) _move_extracted_files( diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index ab1428e5934..cdab0061e34 100755 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -1835,19 +1835,12 @@ def extract_tar(name, directory, tmpdir): extract_dir = os.path.join(tmpdir, "extract_dir") Path(extract_dir).mkdir() - # Extraction filters were added in Python 3.12, - # and backported to 3.8.17, 3.9.17, 3.10.12, and 3.11.4 - # See - # https://docs.python.org/3.12/library/tarfile.html#tarfile-extraction-filter - # and https://peps.python.org/pep-0706/ - # In Python 3.12, using `filter=None` triggers a DepreciationWarning, - # and in Python 3.14, `filter='data'` will be the default - if hasattr(tarfile, "data_filter"): - tar.extractall(path=extract_dir, filter="data") - else: - # Remove this when no longer needed - gs.warning(_("Extracting may be unsafe; consider updating Python")) - tar.extractall(path=extract_dir) + # The 'data' extraction filter was added in Python 3.12 and backported + # to 3.11.4 (PEP 706). Refuse to extract without it rather + # than extracting unsafely. + if not hasattr(tarfile, "data_filter"): + gs.fatal(_("Extracting may be unsafe; upgrade Python to 3.11.4 or newer")) + tar.extractall(path=extract_dir, filter="data") files = os.listdir(extract_dir) move_extracted_files(extract_dir=extract_dir, target_dir=directory, files=files) diff --git a/scripts/r.unpack/r.unpack.py b/scripts/r.unpack/r.unpack.py index 4a60aaf7c06..ee9a5a2b41f 100644 --- a/scripts/r.unpack/r.unpack.py +++ b/scripts/r.unpack/r.unpack.py @@ -106,18 +106,12 @@ def main(): ) # extract data - # Extraction filters were added in Python 3.12, - # and backported to 3.8.17, 3.9.17, 3.10.12, and 3.11.4 - # See https://docs.python.org/3.12/library/tarfile.html#tarfile-extraction-filter - # and https://peps.python.org/pep-0706/ - # In Python 3.12, using `filter=None` triggers a DepreciationWarning, - # and in Python 3.14, `filter='data'` will be the default - if hasattr(tarfile, "data_filter"): - tar.extractall(filter="data") - else: - # Remove this when no longer needed - grass.warning(_("Extracting may be unsafe; consider updating Python")) - tar.extractall() + # The 'data' extraction filter was added in Python 3.12 and backported to + # 3.11.4 (PEP 706). Refuse to extract without it rather than + # extracting unsafely. + if not hasattr(tarfile, "data_filter"): + grass.fatal(_("Extracting may be unsafe; upgrade Python to 3.11.4 or newer")) + tar.extractall(filter="data") tar.close() os.chdir(data_names[0]) diff --git a/scripts/v.unpack/v.unpack.py b/scripts/v.unpack/v.unpack.py index 462f5d3ef1a..b21a150ca60 100644 --- a/scripts/v.unpack/v.unpack.py +++ b/scripts/v.unpack/v.unpack.py @@ -118,18 +118,12 @@ def main(): shutil.rmtree(new_dir, True) # extract data - # Extraction filters were added in Python 3.12, - # and backported to 3.8.17, 3.9.17, 3.10.12, and 3.11.4 - # See https://docs.python.org/3.12/library/tarfile.html#tarfile-extraction-filter - # and https://peps.python.org/pep-0706/ - # In Python 3.12, using `filter=None` triggers a DepreciationWarning, - # and in Python 3.14, `filter='data'` will be the default - if hasattr(tarfile, "data_filter"): - tar.extractall(filter="data") - else: - # Remove this when no longer needed - grass.warning(_("Extracting may be unsafe; consider updating Python")) - tar.extractall() + # The 'data' extraction filter was added in Python 3.12 and backported to + # 3.11.4 (PEP 706). Refuse to extract without it rather than + # extracting unsafely. + if not hasattr(tarfile, "data_filter"): + grass.fatal(_("Extracting may be unsafe; upgrade Python to 3.11.4 or newer")) + tar.extractall(filter="data") tar.close() if Path(data_name, "coor").exists(): pass