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 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
|
||||||
|
|
||||||
|
|
Reference in a new issue