Use safer tar extraction. Handle error messages in the build process more intelligently.

This commit is contained in:
jakedt 2014-04-01 13:46:41 -04:00
parent 4f1ae25128
commit 7c44932c87
2 changed files with 62 additions and 3 deletions

52
util/safetar.py Normal file
View 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)

View file

@ -17,6 +17,7 @@ from data.queue import dockerfile_build_queue
from data import model from data import model
from workers.worker import Worker from workers.worker import Worker
from app import app from app import app
from util.safetar import safe_extractall
root_logger = logging.getLogger('') root_logger = logging.getLogger('')
@ -152,8 +153,14 @@ class DockerfileBuildContext(object):
for status in build_status: for status in build_status:
fully_unwrapped = "" fully_unwrapped = ""
if isinstance(status, dict): if isinstance(status, dict):
if len(status) > 0: keys_to_extract = ['error', 'status', 'stream']
fully_unwrapped = status.values()[0] 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): elif isinstance(status, basestring):
fully_unwrapped = status fully_unwrapped = status
@ -322,7 +329,7 @@ class DockerfileBuildWorker(Worker):
# Save the zip file to temp somewhere # Save the zip file to temp somewhere
with tarfile.open(mode='r|*', fileobj=request_file.raw) as tar_stream: 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 return build_dir