Use safer tar extraction. Handle error messages in the build process more intelligently.
This commit is contained in:
parent
4f1ae25128
commit
7c44932c87
2 changed files with 62 additions and 3 deletions
52
util/safetar.py
Normal file
52
util/safetar.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
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)
|
|
@ -17,6 +17,7 @@ from data.queue import dockerfile_build_queue
|
|||
from data import model
|
||||
from workers.worker import Worker
|
||||
from app import app
|
||||
from util.safetar import safe_extractall
|
||||
|
||||
|
||||
root_logger = logging.getLogger('')
|
||||
|
@ -152,8 +153,14 @@ class DockerfileBuildContext(object):
|
|||
for status in build_status:
|
||||
fully_unwrapped = ""
|
||||
if isinstance(status, dict):
|
||||
if len(status) > 0:
|
||||
fully_unwrapped = status.values()[0]
|
||||
keys_to_extract = ['error', 'status', 'stream']
|
||||
for key in keys_to_extract:
|
||||
if key in status:
|
||||
fully_unwrapped = status[key]
|
||||
break
|
||||
|
||||
if not fully_unwrapped:
|
||||
logger.debug('Status dict did not have any extractable keys and was: %s', status)
|
||||
elif isinstance(status, basestring):
|
||||
fully_unwrapped = status
|
||||
|
||||
|
@ -322,7 +329,7 @@ class DockerfileBuildWorker(Worker):
|
|||
|
||||
# Save the zip file to temp somewhere
|
||||
with tarfile.open(mode='r|*', fileobj=request_file.raw) as tar_stream:
|
||||
tar_stream.extractall(build_dir)
|
||||
safe_extractall(tar_stream, build_dir)
|
||||
|
||||
return build_dir
|
||||
|
||||
|
|
Reference in a new issue