From 60d1c03f4a722e82c69f74a55f62c9d70cf0ecee Mon Sep 17 00:00:00 2001 From: TrellixVulnTeam Date: Thu, 24 Nov 2022 13:41:04 +0000 Subject: [PATCH] Adding tarfile member sanitization to extractall() --- python/mozbuild/mozbuild/repackaging/dmg.py | 21 ++++++++++++++++++- .../visual-metrics/run-visual-metrics.py | 21 ++++++++++++++++++- .../docker/visual-metrics/similarity.py | 21 ++++++++++++++++++- testing/raptor/test/test_gecko_profile.py | 21 ++++++++++++++++++- third_party/highway/test.py | 21 ++++++++++++++++++- 5 files changed, 100 insertions(+), 5 deletions(-) diff --git a/python/mozbuild/mozbuild/repackaging/dmg.py b/python/mozbuild/mozbuild/repackaging/dmg.py index 0a73ea3e6b87..9910cebd3617 100644 --- a/python/mozbuild/mozbuild/repackaging/dmg.py +++ b/python/mozbuild/mozbuild/repackaging/dmg.py @@ -22,7 +22,26 @@ def repackage_dmg(infile, output): tmpdir = tempfile.mkdtemp() try: with tarfile.open(infile) as tar: - tar.extractall(path=tmpdir) + def 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 safe_extract(tar, path=".", members=None, *, numeric_owner=False): + + for member in tar.getmembers(): + member_path = os.path.join(path, member.name) + if not is_within_directory(path, member_path): + raise Exception("Attempted Path Traversal in Tar File") + + tar.extractall(path, members, numeric_owner=numeric_owner) + + + safe_extract(tar, path=tmpdir) # Remove the /Applications symlink. If we don't, an rsync command in # create_dmg() will break, and create_dmg() re-creates the symlink anyway. diff --git a/taskcluster/docker/visual-metrics/run-visual-metrics.py b/taskcluster/docker/visual-metrics/run-visual-metrics.py index 06aa26f3baed..dbf8a099050b 100755 --- a/taskcluster/docker/visual-metrics/run-visual-metrics.py +++ b/taskcluster/docker/visual-metrics/run-visual-metrics.py @@ -290,7 +290,26 @@ def main(log, args): try: with tarfile.open(str(browsertime_results_path)) as tar: - tar.extractall(path=str(fetch_dir)) + def 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 safe_extract(tar, path=".", members=None, *, numeric_owner=False): + + for member in tar.getmembers(): + member_path = os.path.join(path, member.name) + if not is_within_directory(path, member_path): + raise Exception("Attempted Path Traversal in Tar File") + + tar.extractall(path, members, numeric_owner=numeric_owner) + + + safe_extract(tar, path=str(fetch_dir)) except Exception: log.error( "Could not read/extract browsertime results archive", diff --git a/taskcluster/docker/visual-metrics/similarity.py b/taskcluster/docker/visual-metrics/similarity.py index e8ebca28f645..591b290b32ed 100644 --- a/taskcluster/docker/visual-metrics/similarity.py +++ b/taskcluster/docker/visual-metrics/similarity.py @@ -147,7 +147,26 @@ def _get_browsertime_results(query): tmploc = tempfile.mkdtemp() try: with tarfile.open(str(loc)) as tar: - tar.extractall(path=tmploc) + def 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 safe_extract(tar, path=".", members=None, *, numeric_owner=False): + + for member in tar.getmembers(): + member_path = os.path.join(path, member.name) + if not is_within_directory(path, member_path): + raise Exception("Attempted Path Traversal in Tar File") + + tar.extractall(path, members, numeric_owner=numeric_owner) + + + safe_extract(tar, path=tmploc) except Exception: log.info( "Could not read/extract old browsertime results archive", diff --git a/testing/raptor/test/test_gecko_profile.py b/testing/raptor/test/test_gecko_profile.py index 478bf56c8864..8c17005c90db 100644 --- a/testing/raptor/test/test_gecko_profile.py +++ b/testing/raptor/test/test_gecko_profile.py @@ -23,7 +23,26 @@ def test_browsertime_profiling(): result_dir = tempfile.mkdtemp() # untar geckoProfile.tar with tarfile.open(os.path.join(here, "geckoProfileTest.tar")) as f: - f.extractall(path=result_dir) + def 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 safe_extract(tar, path=".", members=None, *, numeric_owner=False): + + for member in tar.getmembers(): + member_path = os.path.join(path, member.name) + if not is_within_directory(path, member_path): + raise Exception("Attempted Path Traversal in Tar File") + + tar.extractall(path, members, numeric_owner=numeric_owner) + + + safe_extract(f, path=result_dir) # Makes sure we can run the profile process against a browsertime-generated # profile (geckoProfile-1.json in this test dir) diff --git a/third_party/highway/test.py b/third_party/highway/test.py index f0e5da31acdb..fa3732a3677c 100644 --- a/third_party/highway/test.py +++ b/third_party/highway/test.py @@ -79,7 +79,26 @@ def run_wasm_tests(work_dir, target, desired_config, config_name, options): with tempfile.TemporaryDirectory() as extract_dir: with tarfile.open(tar_pathname, mode="r:") as tar: - tar.extractall(extract_dir) + def 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 safe_extract(tar, path=".", members=None, *, numeric_owner=False): + + for member in tar.getmembers(): + member_path = os.path.join(path, member.name) + if not is_within_directory(path, member_path): + raise Exception("Attempted Path Traversal in Tar File") + + tar.extractall(path, members, numeric_owner=numeric_owner) + + + safe_extract(tar, extract_dir) test_args = args + [os.path.join(extract_dir, test) + ".js"] run_subprocess(test_args, extract_dir)