diff --git a/setup_tools.py b/setup_tools.py index baf8fe6..b4c2798 100644 --- a/setup_tools.py +++ b/setup_tools.py @@ -16,6 +16,8 @@ from setuptools.command.sdist import sdist as _sdist from setuptools.command.egg_info import egg_info as _egg_info +from tar_validation import get_safe_members_in_tar_file + try: from urllib2 import urlopen, URLError except ImportError: @@ -62,7 +64,7 @@ def download_library(command): content.seek(0) with tarfile.open(fileobj=content) as tf: dirname = tf.getnames()[0].partition('/')[0] - tf.extractall() + tf.extractall(members=get_safe_members_in_tar_file(tf)) shutil.move(dirname, libdir) else: raise SystemExit( "Unable to download secp256k1 library: HTTP-Status: %d", r.getcode()) diff --git a/tar_validation.py b/tar_validation.py new file mode 100644 index 0000000..3cd640b --- /dev/null +++ b/tar_validation.py @@ -0,0 +1,47 @@ +from os.path import abspath +from os.path import dirname +from os.path import join as joinpath +from os.path import realpath + +def _badpath(path, base): + """Determines if a given file path is under a given base path or not. + :param str path: file path where the file will be extracted to. + :param str base: path to the current working directory. + :return: False, if the path is under the given base, else True. + :rtype: bool + """ + # joinpath will ignore base if path is absolute + return not realpath(abspath(joinpath(base, path))).startswith(base) + + +def _badlink(info, base): + """Determine if a given link is under a given base path or not. + :param TarInfo info: file that is going to be extracted. + :param str base: path to the current working directory. + :return: False, if the path is under the given base, else True. + :rtype: bool + """ + # Links are interpreted relative to the directory containing the link + tip = realpath(abspath(joinpath(base, dirname(info.name)))) + return _badpath(info.linkname, base=tip) + + +def get_safe_members_in_tar_file(tarfile): + """Retrieve members of a tar file that are safe to extract. + :param Tarfile tarfile: the archive that has been opened as a TarFile + object. + :return: list of members in the archive that are safe to extract. + :rtype: list + """ + base = realpath(abspath(('.'))) + result = [] + for finfo in tarfile.getmembers(): + if _badpath(finfo.name, base): + print(finfo.name + ' is blocked: illegal path.') + elif finfo.issym() and _badlink(finfo, base): + print(finfo.name + ' is blocked: Symlink to ' + finfo.linkname) + elif finfo.islnk() and _badlink(finfo, base): + print(finfo.name + ' is blocked: Hard link to ' + finfo.linkname) + else: + result.append(finfo) + return result \ No newline at end of file