diff --git a/python/mozbuild/mozbuild/repackaging/dmg.py b/python/mozbuild/mozbuild/repackaging/dmg.py index a80fcab8df..28153a0c5e 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 b55413aaa1..828603d9bf 100644 --- a/taskcluster/docker/visual-metrics/run-visual-metrics.py +++ b/taskcluster/docker/visual-metrics/run-visual-metrics.py @@ -201,7 +201,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 f56e158758..835c9f8382 100644 --- a/taskcluster/docker/visual-metrics/similarity.py +++ b/taskcluster/docker/visual-metrics/similarity.py @@ -145,7 +145,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 9e3fdb74ce..1bce43e5b6 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, "geckoProfile.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)