import os.path import logging import copy import operator logger = logging.getLogger(__name__) def safe_extractall(tar, path): """Extract all members from the archive to the current working directory and set owner, modification time and permissions on directories afterwards. `path' specifies a different directory to extract to. `members' is optional and must be a subset of the list returned by getmembers(). """ abs_extract_path = os.path.abspath(path) def is_member_safe(member): """ Returns True if the member is safe, False otherwise. """ if member.issym(): link_tar_path = os.path.dirname(member.path) abs_link_path = os.path.abspath(os.path.join(path, link_tar_path, member.linkpath)) if not abs_link_path.startswith(abs_extract_path): logger.warning('Filtering symlink outside of extract dir: %s', member.linkpath) return False elif member.isblk(): logger.warning('Filtering block device from tarfile: %s', member.path) return False return True directories = [] for tarinfo in tar: if is_member_safe(tarinfo): if tarinfo.isdir(): # Extract directories with a safe mode. directories.append(tarinfo) tarinfo = copy.copy(tarinfo) tarinfo.mode = 0700 tar.extract(tarinfo, path) # Reverse sort directories. directories.sort(key=operator.attrgetter('name')) directories.reverse() # Set correct owner, mtime and filemode on directories. for tarinfo in directories: dirpath = os.path.join(path, tarinfo.name) tar.chown(tarinfo, dirpath) tar.utime(tarinfo, dirpath) tar.chmod(tarinfo, dirpath)