From a90349e976bde1173fae27c092e4e3670bf73d7c Mon Sep 17 00:00:00 2001 From: nussaiba shaikh Date: Wed, 28 Oct 2026 02:25:38 +0530 Subject: [PATCH] donate-cpu: keep extracted package files inside the temp folder --- test/tools/donate_cpu_lib_test.py | 30 ++++++++++++++++++++++++++++++ tools/donate_cpu_lib.py | 28 ++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/test/tools/donate_cpu_lib_test.py b/test/tools/donate_cpu_lib_test.py index 9c569276a19..2158d2942ca 100644 --- a/test/tools/donate_cpu_lib_test.py +++ b/test/tools/donate_cpu_lib_test.py @@ -16,6 +16,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import io +import tarfile + from donate_cpu_lib import * def _test_library_includes(tmpdir, libs, content, libinc_obj=None): @@ -66,3 +69,30 @@ def test_match_multiple_time(tmpdir): # there was a bug that we would only match each library once successfully _test_library_includes(tmpdir, ['posix', 'gnu', 'bsd', 'zlib'], '#include ', libinc) _test_library_includes(tmpdir, ['posix', 'gnu', 'bsd', 'zlib'], '#include ', libinc) + +def _add_tar_member(tf, name, linkname=None): + info = tarfile.TarInfo(name) + data = b'' + if linkname is not None: + info.type = tarfile.SYMTYPE + info.linkname = linkname + else: + data = b'int x;\n' + info.size = len(data) + tf.addfile(info, io.BytesIO(data)) + +def test_unpack_package_outside_temp(tmpdir): + work_path = str(tmpdir) + outside_path = os.path.join(work_path, 'outside') + os.mkdir(outside_path) + tgz = os.path.join(work_path, 'temp.tgz') + with tarfile.open(tgz, 'w:gz') as tf: + _add_tar_member(tf, 'pkg/main.c') + _add_tar_member(tf, 'pkg/../../outside/dotdot.c') + _add_tar_member(tf, 'pkg/inc.h', linkname=outside_path) + _add_tar_member(tf, 'pkg/inc.h/symlink.c') + + temp_path, source_found = unpack_package(work_path, tgz) + assert source_found + assert os.path.isfile(os.path.join(temp_path, 'pkg', 'main.c')) + assert os.listdir(outside_path) == [] diff --git a/tools/donate_cpu_lib.py b/tools/donate_cpu_lib.py index ed345cb10c9..92877eb5b50 100644 --- a/tools/donate_cpu_lib.py +++ b/tools/donate_cpu_lib.py @@ -16,7 +16,7 @@ # Version scheme (MAJOR.MINOR.PATCH) should orientate on "Semantic Versioning" https://semver.org/ # Every change in this script should result in increasing the version number accordingly (exceptions may be cosmetic # changes) -CLIENT_VERSION = "1.4.0" +CLIENT_VERSION = "1.4.1" # Timeout for analysis with Cppcheck in seconds CPPCHECK_TIMEOUT = 30 * 60 @@ -344,10 +344,19 @@ def download_package(work_path, package, bandwidth_limit): return destfile +def __is_inside_path(base_path, path): + try: + return os.path.commonpath([base_path, path]) == base_path + except ValueError: + # paths are on different drives + return False + + def unpack_package(work_path, tgz, cpp_only=False, c_only=False, skip_files=None): temp_path = os.path.join(work_path, 'temp') __remove_tree(temp_path) os.mkdir(temp_path) + real_temp_path = os.path.realpath(temp_path) header_endings = ('.hpp', '.h++', '.hxx', '.hh', '.h') cpp_source_endings = ('.cpp', '.c++', '.cxx', '.cc', '.tpp', '.txx', '.ipp', '.ixx', '.qml') @@ -367,10 +376,19 @@ def unpack_package(work_path, tgz, cpp_only=False, c_only=False, skip_files=None total = 0 extracted = 0 skipped = 0 + # Python versions with extraction filters reject members that leave temp_path as well + extract_args = {'filter': 'data'} if hasattr(tarfile, 'data_filter') else {} for member in tf: total += 1 - if member.name.startswith(('/', '..')): - # Skip dangerous file names + member_path = os.path.realpath(os.path.join(temp_path, member.name)) + link_path = None + if member.issym(): + link_path = os.path.realpath(os.path.join(os.path.dirname(member_path), member.linkname)) + elif member.islnk(): + link_path = os.path.realpath(os.path.join(temp_path, member.linkname)) + if not __is_inside_path(real_temp_path, member_path) or \ + (link_path is not None and not __is_inside_path(real_temp_path, link_path)): + # Skip dangerous file names and links pointing outside of temp_path # print('skipping dangerous file: ' + member.name) skipped += 1 continue @@ -388,7 +406,7 @@ def unpack_package(work_path, tgz, cpp_only=False, c_only=False, skip_files=None skipped += 1 continue try: - tf.extract(member.name, temp_path) + tf.extract(member.name, temp_path, **extract_args) if is_source: source_found = True extracted += 1 @@ -396,6 +414,8 @@ def unpack_package(work_path, tgz, cpp_only=False, c_only=False, skip_files=None pass except AttributeError: pass + except tarfile.TarError: + pass print('extracted {} of {} files (skipped {}{})'.format(extracted, total, skipped, (' / only headers' if (extracted and not source_found) else ''))) return temp_path, source_found