Repository: adobe/buildrunner (language: python)

Issue:
CVE-2007-4559 Patch
# Patching CVE-2007-4559

Hi, we are security researchers from the Advanced Research Center at [Trellix](https://www.trellix.com). We have began a campaign to patch a widespread bug named CVE-2007-4559. CVE-2007-4559 is a 15 year old bug in the Python tarfile package. By using extract() or extractall() on a tarfile object without sanitizing input, a maliciously crafted .tar file could perform a directory path traversal attack. We found at least one unsantized extractall() in your codebase and are providing a patch for you via pull request. The patch essentially checks to see if all tarfile members will be extracted safely and throws an exception otherwise. We encourage you to use this patch or your own solution to secure against CVE-2007-4559. Further technical information about the vulnerability can be found in this [blog](https://www.trellix.com/en-us/about/newsroom/stories/research/tarfile-exploiting-the-world.html).

If you have further questions you may contact us through this projects lead researcher [Kasimir Schulz](mailto:kasimir.schulz@trellix.com).



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index a1d21e5f..286a9c11 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -75,9 +75,9 @@ jobs:
       # This is just in case something else created it, destroy it to get a fresh version
       run: rm -f buildrunner/version.py
     - name: Install wheel
-      run: pip install wheel
+      run: pip install wheel build
     - name: Build
-      run: python setup.py sdist bdist_wheel
+      run: python -m build
     - name: Check upload
       run: pip install twine && twine check dist/*
     - name: Publish to PyPi


Test patch:
diff --git a/tests/test_caching.py b/tests/test_caching.py
index 83e5b7fb..a3119af3 100644
--- a/tests/test_caching.py
+++ b/tests/test_caching.py
@@ -13,6 +13,21 @@
 import pytest
 
 
+def _tar_is_within_directory(directory, target):
+    abs_directory = os.path.abspath(directory)
+    abs_target = os.path.abspath(target)
+    prefix = os.path.commonprefix([abs_directory, abs_target])
+    return prefix == abs_directory
+
+
+def _tar_safe_extractall(tar, path=".", members=None, *, numeric_owner=False):
+    for member in tar.getmembers():
+        member_path = os.path.join(path, member.name)
+        if not _tar_is_within_directory(path, member_path):
+            raise Exception("Attempted path traversal in tar file")
+    tar.extractall(path, members, numeric_owner=numeric_owner)
+
+
 @pytest.fixture(name="runner")
 def fixture_setup_runner():
     image_config = DockerRunner.ImageConfig(
@@ -276,7 +291,7 @@ def test_save_cache_basic(runner, tmp_dir_name, mock_logger):
     extracted_dir = "extracted_data"
     os.mkdir(extracted_dir)
     with tarfile.open(tarfile_name) as tar:
-        tar.extractall(extracted_dir)
+        _tar_safe_extractall(tar, extracted_dir)
         extracted_files = os.listdir(extracted_dir)
 
         assert len(test_files) == len(extracted_files)
@@ -323,7 +338,7 @@ def test_save_cache_multiple_cache_keys(runner, tmp_dir_name, mock_logger):
     extracted_dir = "extracted_data"
     os.mkdir(extracted_dir)
     with tarfile.open(tarfile_name) as tar:
-        tar.extractall(extracted_dir)
+        _tar_safe_extractall(tar, extracted_dir)
         extracted_files = os.listdir(extracted_dir)
 
         assert len(test_files) == len(extracted_files)
@@ -347,7 +362,7 @@ def test_save_cache_multiple_cache_keys(runner, tmp_dir_name, mock_logger):
     extracted_dir = "extracted_data2"
     os.mkdir(extracted_dir)
     with tarfile.open(tarfile_name) as tar:
-        tar.extractall(extracted_dir)
+        _tar_safe_extractall(tar, extracted_dir)
         extracted_files = os.listdir(extracted_dir)
 
         assert len(test_files) == len(extracted_files)
@@ -386,7 +401,7 @@ def test_save_cache_multiple_caches(runner, tmp_dir_name, mock_logger):
 
     os.mkdir(venv_extracted_dir)
     with tarfile.open(venv_tarfile_name) as tar:
-        tar.extractall(venv_extracted_dir)
+        _tar_safe_extractall(tar, venv_extracted_dir)
         extracted_files = os.listdir(venv_extracted_dir)
 
         assert len(venv_test_files) == len(extracted_files)
@@ -397,7 +412,7 @@ def test_save_cache_multiple_caches(runner, tmp_dir_name, mock_logger):
 
     os.mkdir(maven_extracted_dir)
     with tarfile.open(maven_tarfile_name) as tar:
-        tar.extractall(maven_extracted_dir)
+        _tar_safe_extractall(tar, maven_extracted_dir)
         extracted_files = os.listdir(maven_extracted_dir)
 
         assert len(maven_test_files) == len(extracted_files)