From 928e391c456d187c41e74b3c9fc7a372a27e8f8f Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Thu, 25 May 2023 11:21:53 -0400 Subject: [PATCH] Enable new tarfile extraction protections in Python 3.12 --- pyproject.toml | 1 + src/briefcase/commands/create.py | 10 ++++++++++ src/briefcase/integrations/android_sdk.py | 7 ++++++- .../create/test_install_app_support_package.py | 3 +++ .../AndroidSDK/test_verify_emulator_skin.py | 3 +++ 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1e76b3084..052c5ea8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ no-cover-if-is-macos = "'darwin' == os_environ.get('COVERAGE_PLATFORM', sys_plat no-cover-if-not-macos = "'darwin' != os_environ.get('COVERAGE_PLATFORM', sys_platform) and os_environ.get('COVERAGE_EXCLUDE_PLATFORM') != 'disable'" no-cover-if-is-windows = "'win32' == os_environ.get('COVERAGE_PLATFORM', sys_platform) and os_environ.get('COVERAGE_EXCLUDE_PLATFORM') != 'disable'" no-cover-if-not-windows = "'win32' != os_environ.get('COVERAGE_PLATFORM', sys_platform) and os_environ.get('COVERAGE_EXCLUDE_PLATFORM') != 'disable'" +no-cover-if-lt-py312 = "sys_version_info < (3, 12) and os_environ.get('COVERAGE_EXCLUDE_PYTHON_VERSION') != 'disable'" no-cover-if-is-py310 = "python_version == '3.10' and os_environ.get('COVERAGE_EXCLUDE_PYTHON_VERSION') != 'disable'" no-cover-if-lt-py310 = "sys_version_info < (3, 10) and os_environ.get('COVERAGE_EXCLUDE_PYTHON_VERSION') != 'disable'" no-cover-if-gte-py310 = "sys_version_info > (3, 10) and os_environ.get('COVERAGE_EXCLUDE_PYTHON_VERSION') != 'disable'" diff --git a/src/briefcase/commands/create.py b/src/briefcase/commands/create.py index ceb9d38d9..8410637e0 100644 --- a/src/briefcase/commands/create.py +++ b/src/briefcase/commands/create.py @@ -283,12 +283,22 @@ def _unpack_support_package(self, support_file_path, support_path): :param support_file_path: The path to the support file to be unpacked. :param support_path: The path where support files should be unpacked. """ + # Additional protections for unpacking tar files were introduced in Python 3.12. + # This enables the behavior that will be the default in Python 3.14. + # However, the protections can only be enabled for tar files...not zip files. + is_zip = support_file_path.name.endswith("zip") + if sys.version_info >= (3, 12) and not is_zip: # pragma: no-cover-if-lt-py312 + tarfile_kwargs = {"filter": "data"} + else: + tarfile_kwargs = {} + try: with self.input.wait_bar("Unpacking support package..."): support_path.mkdir(parents=True, exist_ok=True) self.tools.shutil.unpack_archive( support_file_path, extract_dir=support_path, + **tarfile_kwargs, ) except (shutil.ReadError, EOFError) as e: raise InvalidSupportPackage(support_file_path) from e diff --git a/src/briefcase/integrations/android_sdk.py b/src/briefcase/integrations/android_sdk.py index 0d090ef23..deb1f08c4 100644 --- a/src/briefcase/integrations/android_sdk.py +++ b/src/briefcase/integrations/android_sdk.py @@ -4,6 +4,7 @@ import shlex import shutil import subprocess +import sys import time from contextlib import suppress from pathlib import Path @@ -604,7 +605,11 @@ def verify_emulator_skin(self, skin): # Unpack skin archive with self.tools.input.wait_bar("Installing device skin..."): try: - self.tools.shutil.unpack_archive(skin_tgz_path, extract_dir=skin_path) + self.tools.shutil.unpack_archive( + skin_tgz_path, + extract_dir=skin_path, + **({"filter": "data"} if sys.version_info >= (3, 12) else {}), + ) except (shutil.ReadError, EOFError) as err: raise BriefcaseCommandError( f"Unable to unpack {skin} device skin." diff --git a/tests/commands/create/test_install_app_support_package.py b/tests/commands/create/test_install_app_support_package.py index 4b286ae95..3d7205ba7 100644 --- a/tests/commands/create/test_install_app_support_package.py +++ b/tests/commands/create/test_install_app_support_package.py @@ -1,5 +1,6 @@ import os import shutil +import sys from unittest import mock import pytest @@ -55,6 +56,7 @@ def test_install_app_support_package( create_command.tools.shutil.unpack_archive.assert_called_with( tmp_path / "data" / "support" / "Python-3.X-tester-support.b37.tar.gz", extract_dir=support_path, + **({"filter": "data"} if sys.version_info >= (3, 12) else {}), ) # Confirm that the full path to the support file @@ -100,6 +102,7 @@ def test_install_pinned_app_support_package( create_command.tools.shutil.unpack_archive.assert_called_with( tmp_path / "data" / "support" / "Python-3.X-Tester-support.b42.tar.gz", extract_dir=support_path, + **({"filter": "data"} if sys.version_info >= (3, 12) else {}), ) # Confirm that the full path to the support file diff --git a/tests/integrations/android_sdk/AndroidSDK/test_verify_emulator_skin.py b/tests/integrations/android_sdk/AndroidSDK/test_verify_emulator_skin.py index 2981f4581..35363d6d4 100644 --- a/tests/integrations/android_sdk/AndroidSDK/test_verify_emulator_skin.py +++ b/tests/integrations/android_sdk/AndroidSDK/test_verify_emulator_skin.py @@ -1,3 +1,4 @@ +import sys from pathlib import Path from unittest.mock import MagicMock @@ -42,6 +43,7 @@ def test_new_skin(mock_tools, android_sdk): mock_tools.shutil.unpack_archive.assert_called_once_with( skin_tgz_path, extract_dir=android_sdk.root_path / "skins" / "pixel_X", + **({"filter": "data"} if sys.version_info >= (3, 12) else {}), ) # Original file was deleted. @@ -108,6 +110,7 @@ def test_unpack_failure(mock_tools, android_sdk, tmp_path): mock_tools.shutil.unpack_archive.assert_called_once_with( skin_tgz_path, extract_dir=android_sdk.root_path / "skins" / "pixel_X", + **({"filter": "data"} if sys.version_info >= (3, 12) else {}), ) # Original file wasn't deleted.