Merge pull request #2115 from jmchilton/dev-merge

Merge release into latest dev.
This commit is contained in:
Martin Cech
2016-04-08 14:29:18 -04:00
3 changed files with 41 additions and 15 deletions
@@ -442,7 +442,7 @@ class AdminToolshed( AdminGalaxy ):
# Avoid caching
trans.response.headers['Pragma'] = 'no-cache'
trans.response.headers['Expires'] = '0'
return suc.get_repository_file_contents( trans.app, file_path, repository_id )
return suc.get_repository_file_contents( trans.app, file_path, repository_id, is_admin=True )
@web.expose
@web.require_admin
@@ -973,7 +973,7 @@ class AdminToolshed( AdminGalaxy ):
# Avoid caching
trans.response.headers['Pragma'] = 'no-cache'
trans.response.headers['Expires'] = '0'
return suc.open_repository_files_folder( trans.app, folder_path, repository_id )
return suc.open_repository_files_folder( trans.app, folder_path, repository_id, is_admin=True )
@web.expose
@web.require_admin
@@ -1471,7 +1471,8 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
# Avoid caching
trans.response.headers['Pragma'] = 'no-cache'
trans.response.headers['Expires'] = '0'
return suc.get_repository_file_contents( trans.app, file_path, repository_id )
is_admin = trans.user_is_admin()
return suc.get_repository_file_contents( trans.app, file_path, repository_id, is_admin )
@web.expose
def get_functional_test_rss( self, trans, **kwd ):
@@ -2424,7 +2425,8 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
# Avoid caching
trans.response.headers['Pragma'] = 'no-cache'
trans.response.headers['Expires'] = '0'
return suc.open_repository_files_folder( trans.app, folder_path, repository_id )
is_admin = trans.user_is_admin()
return suc.open_repository_files_folder( trans.app, folder_path, repository_id, is_admin )
@web.expose
def preview_tools_in_changeset( self, trans, repository_id, **kwd ):
+35 -11
View File
@@ -636,13 +636,13 @@ def get_repository_for_dependency_relationship( app, tool_shed, name, owner, cha
return repository
def get_repository_file_contents( app, file_path, repository_id ):
def get_repository_file_contents( app, file_path, repository_id, is_admin=False ):
"""Return the display-safe contents of a repository file for display in a browser."""
safe_str = ''
if not is_path_within_repo( app, file_path, repository_id ):
if not is_path_browsable( app, file_path, repository_id, is_admin ):
log.warning( 'Request tries to access a file outside of the repository location. File path: %s', file_path )
return 'Invalid file path'
# Symlink targets are checked by is_path_within_repo
# Symlink targets are checked by is_path_browsable
if os.path.islink( file_path ):
safe_str = 'link to: ' + basic_util.to_html_string( os.readlink( file_path ) )
return safe_str
@@ -1150,14 +1150,13 @@ def is_tool_shed_client( app ):
return hasattr( app, "install_model" )
def open_repository_files_folder( app, folder_path, repository_id ):
def open_repository_files_folder( app, folder_path, repository_id, is_admin=False ):
"""
Return a list of dictionaries, each of which contains information for a file or directory contained
within a directory in a repository file hierarchy.
"""
# Symlink targets are checked by is_path_within_repo
if not is_path_within_repo( app, folder_path, repository_id ):
log.warning( 'Request tries to access a folder outside of the repository location. Folder path: %s', folder_path )
if not is_path_browsable( app, folder_path, repository_id, is_admin ):
log.warning( 'Request tries to access a folder outside of the allowed locations. Folder path: %s', folder_path )
return []
try:
files_list = get_repository_files( folder_path )
@@ -1170,11 +1169,11 @@ def open_repository_files_folder( app, folder_path, repository_id ):
is_folder = False
full_path = os.path.join( folder_path, filename )
is_link = os.path.islink( full_path )
path_is_within_repo = is_path_within_repo( app, full_path, repository_id )
if is_link and not path_is_within_repo:
path_is_browsable = is_path_browsable( app, full_path, repository_id )
if is_link and not path_is_browsable:
log.warning( 'Valid folder contains a symlink outside of the repository location. Link found in: ' + str( full_path ) )
if filename:
if os.path.isdir( full_path ) and path_is_within_repo:
if os.path.isdir( full_path ) and path_is_browsable:
# Append a '/' character so that our jquery dynatree will function properly.
filename = '%s/' % filename
full_path = '%s/' % full_path
@@ -1188,9 +1187,20 @@ def open_repository_files_folder( app, folder_path, repository_id ):
return folder_contents
def is_path_browsable( app, path, repository_id, is_admin=False ):
"""
Detects whether the given path is browsable i.e. is within the
allowed repository folders. Admins can additionaly browse folders
with tool dependencies.
"""
if is_admin and is_path_within_dependency_dir( app, path ):
return True
return is_path_within_repo( app, path, repository_id)
def is_path_within_repo( app, path, repository_id ):
"""
Detect whether the given path is within the repository folde ron the disk.
Detect whether the given path is within the repository folder on the disk.
Use to filter malicious symlinks targeting outside paths.
"""
repo_path = os.path.abspath( get_repository_by_id( app, repository_id ).repo_path( app ) )
@@ -1198,6 +1208,20 @@ def is_path_within_repo( app, path, repository_id ):
return os.path.commonprefix( [ repo_path, resolved_path ] ) == repo_path
def is_path_within_dependency_dir( app, path ):
"""
Detect whether the given path is within the tool_dependency_dir folder on the disk.
(Specified by the config option). Use to filter malicious symlinks targeting outside paths.
"""
allowed = False
resolved_path = os.path.realpath( path )
tool_dependency_dir = app.config.get( 'tool_dependency_dir', None )
if tool_dependency_dir:
dependency_path = os.path.abspath( tool_dependency_dir )
allowed = os.path.commonprefix( [ dependency_path, resolved_path ] ) == dependency_path
return allowed
def repository_was_previously_installed( app, tool_shed_url, repository_name, repo_info_tuple, from_tip=False ):
"""
Find out if a repository is already installed into Galaxy - there are several scenarios where this