Skip to content

Commit a90349e

Browse files
committed
donate-cpu: keep extracted package files inside the temp folder
1 parent a573ac3 commit a90349e

2 files changed

Lines changed: 54 additions & 4 deletions

File tree

‎test/tools/donate_cpu_lib_test.py‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
# You should have received a copy of the GNU General Public License
1717
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1818

19+
import io
20+
import tarfile
21+
1922
from donate_cpu_lib import *
2023

2124
def _test_library_includes(tmpdir, libs, content, libinc_obj=None):
@@ -66,3 +69,30 @@ def test_match_multiple_time(tmpdir):
6669
# there was a bug that we would only match each library once successfully
6770
_test_library_includes(tmpdir, ['posix', 'gnu', 'bsd', 'zlib'], '#include <zlib.h>', libinc)
6871
_test_library_includes(tmpdir, ['posix', 'gnu', 'bsd', 'zlib'], '#include <zlib.h>', libinc)
72+
73+
def _add_tar_member(tf, name, linkname=None):
74+
info = tarfile.TarInfo(name)
75+
data = b''
76+
if linkname is not None:
77+
info.type = tarfile.SYMTYPE
78+
info.linkname = linkname
79+
else:
80+
data = b'int x;\n'
81+
info.size = len(data)
82+
tf.addfile(info, io.BytesIO(data))
83+
84+
def test_unpack_package_outside_temp(tmpdir):
85+
work_path = str(tmpdir)
86+
outside_path = os.path.join(work_path, 'outside')
87+
os.mkdir(outside_path)
88+
tgz = os.path.join(work_path, 'temp.tgz')
89+
with tarfile.open(tgz, 'w:gz') as tf:
90+
_add_tar_member(tf, 'pkg/main.c')
91+
_add_tar_member(tf, 'pkg/../../outside/dotdot.c')
92+
_add_tar_member(tf, 'pkg/inc.h', linkname=outside_path)
93+
_add_tar_member(tf, 'pkg/inc.h/symlink.c')
94+
95+
temp_path, source_found = unpack_package(work_path, tgz)
96+
assert source_found
97+
assert os.path.isfile(os.path.join(temp_path, 'pkg', 'main.c'))
98+
assert os.listdir(outside_path) == []

‎tools/donate_cpu_lib.py‎

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# Version scheme (MAJOR.MINOR.PATCH) should orientate on "Semantic Versioning" https://semver.org/
1717
# Every change in this script should result in increasing the version number accordingly (exceptions may be cosmetic
1818
# changes)
19-
CLIENT_VERSION = "1.4.0"
19+
CLIENT_VERSION = "1.4.1"
2020

2121
# Timeout for analysis with Cppcheck in seconds
2222
CPPCHECK_TIMEOUT = 30 * 60
@@ -344,10 +344,19 @@ def download_package(work_path, package, bandwidth_limit):
344344
return destfile
345345

346346

347+
def __is_inside_path(base_path, path):
348+
try:
349+
return os.path.commonpath([base_path, path]) == base_path
350+
except ValueError:
351+
# paths are on different drives
352+
return False
353+
354+
347355
def unpack_package(work_path, tgz, cpp_only=False, c_only=False, skip_files=None):
348356
temp_path = os.path.join(work_path, 'temp')
349357
__remove_tree(temp_path)
350358
os.mkdir(temp_path)
359+
real_temp_path = os.path.realpath(temp_path)
351360

352361
header_endings = ('.hpp', '.h++', '.hxx', '.hh', '.h')
353362
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
367376
total = 0
368377
extracted = 0
369378
skipped = 0
379+
# Python versions with extraction filters reject members that leave temp_path as well
380+
extract_args = {'filter': 'data'} if hasattr(tarfile, 'data_filter') else {}
370381
for member in tf:
371382
total += 1
372-
if member.name.startswith(('/', '..')):
373-
# Skip dangerous file names
383+
member_path = os.path.realpath(os.path.join(temp_path, member.name))
384+
link_path = None
385+
if member.issym():
386+
link_path = os.path.realpath(os.path.join(os.path.dirname(member_path), member.linkname))
387+
elif member.islnk():
388+
link_path = os.path.realpath(os.path.join(temp_path, member.linkname))
389+
if not __is_inside_path(real_temp_path, member_path) or \
390+
(link_path is not None and not __is_inside_path(real_temp_path, link_path)):
391+
# Skip dangerous file names and links pointing outside of temp_path
374392
# print('skipping dangerous file: ' + member.name)
375393
skipped += 1
376394
continue
@@ -388,14 +406,16 @@ def unpack_package(work_path, tgz, cpp_only=False, c_only=False, skip_files=None
388406
skipped += 1
389407
continue
390408
try:
391-
tf.extract(member.name, temp_path)
409+
tf.extract(member.name, temp_path, **extract_args)
392410
if is_source:
393411
source_found = True
394412
extracted += 1
395413
except OSError:
396414
pass
397415
except AttributeError:
398416
pass
417+
except tarfile.TarError:
418+
pass
399419
print('extracted {} of {} files (skipped {}{})'.format(extracted, total, skipped, (' / only headers' if (extracted and not source_found) else '')))
400420
return temp_path, source_found
401421

0 commit comments

Comments
 (0)