CVE-2015-10062 (Python, published 2023-01-17T19:15Z)
Description: value: A vulnerability, which was classified as problematic, was found in galaxy-data-resource up to 14.10.0. This affects an unknown part of the component Command Line Template. The manipulation leads to injection. Upgrading to version 14.10.1 is able to address this issue. The patch is named 50d65f45d3f5be5d1fbff2e45ac5cec075f07d42. It is recommended to upgrade the affected component. The associated identifier of this vulnerability is VDB-218451.
Severity: nan (CVSS v3 base score: 9.8)
Weakness: CWE-74 \u2014 Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')
The product constructs all or part of a command, data structure, or record using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify how it is parsed or interpreted when it is sent to a downstream component.
Repository: https://github.com/blankenberg/galaxy-data-resource
Files changed:
lib/galaxy/datatypes/metadata.py
lib/galaxy/tools/evaluation.py
lib/galaxy/tools/wrappers.py
lib/galaxy/util/__init__.py
lib/galaxy/util/dbkeys.py
lib/galaxy/util/object_wrapper.py
Commit message: Fix a critical security vulnerability where unsanitized user-modifiable values could be included in a command line template.


Vulnerable code:
                rval = self.metadata.spec[name].param.to_string( rval )
            self.dataset = NoneDataset( datatypes_registry=datatypes_registry, ext=ext )
            self.dataset = dataset
            return self.dataset.file_name
                    return self.dataset.extra_files_path
def restore_text(text):
    for key, value in mapped_chars.items():
def sanitize_text(text):
    and lists of strings.
    if isinstance( text, basestring ):
        return _sanitize_text_helper(text)
    elif isinstance( text, list ):
        return [ _sanitize_text_helper(t) for t in text ]

def _sanitize_text_helper(text):
        if c in valid_chars:
        elif c in mapped_chars:
            out.append(mapped_chars[c])
            out.append('X')  # makes debugging easier
def sanitize_param(value):
        return sanitize_text(value)
        return map(sanitize_text, value)
            chrom_info = os.path.join( self._static_chrom_info_path, "%s.len" % dbkey )

Fixed code:
from galaxy.util.object_wrapper import sanitize_lists_to_string
    def to_safe_string( self, value ):
        return sanitize_lists_to_string( self.to_string( value ) )

    def to_safe_string( self, value ):
        # We do not sanitize json dicts
        return json.safe_dumps( value )

    def to_safe_string( self, value ):
        # We do not sanitize file names
        return self.to_string( value )

from galaxy.util.object_wrapper import wrap_with_safe_string
    ToolParameterValueWrapper,
        # Call param dict sanitizer, before non-job params are added, as we don't want to sanitize filenames.
        self.__sanitize_param_dict( param_dict )
        # Parameters added after this line are not sanitized
    def __sanitize_param_dict( self, param_dict ):
        """
        Sanitize all values that will be substituted on the command line, with the exception of ToolParameterValueWrappers,
        which already have their own specific sanitization rules and also exclude special-cased named values.
        We will only examine the first level for values to skip; the wrapping function will recurse as necessary.
        
        Note: this method follows the style of the similar populate calls, in that param_dict is modified in-place.
        """
        # chromInfo is a filename, do not sanitize it.
        skip = [ 'chromInfo' ]
        if not self.tool or not self.tool.options or self.tool.options.sanitize:
            for key, value in param_dict.items():
                if key not in skip:
                    # Remove key so that new wrapped object will occupy key slot
                    del param_dict[key]
                    # And replace with new wrapped key
                    param_dict[ wrap_with_safe_string( key, no_wrap_classes=ToolParameterValueWrapper ) ] = wrap_with_safe_string( value, no_wrap_classes=ToolParameterValueWrapper )

from galaxy.util.object_wrapper import wrap_with_safe_string
                rval = self.metadata.spec[ name ].param.to_safe_string( rval )
            else:
                #escape string value of non-defined metadata value
                rval = wrap_with_safe_string( rval )
            self.dataset = wrap_with_safe_string( NoneDataset( datatypes_registry=datatypes_registry, ext=ext ), no_wrap_classes=ToolParameterValueWrapper )
            # Tool wrappers should not normally be accessing .dataset directly, 
            # so we will wrap it and keep the original around for file paths
            # Should we name this .value to maintain consistency with most other ToolParameterValueWrapper?
            self.unsanitized = dataset
            self.dataset = wrap_with_safe_string( dataset, no_wrap_classes=ToolParameterValueWrapper )
            return self.unsanitized.file_name
                    return self.unsanitized.extra_files_path
def restore_text( text, character_map=mapped_chars ):
    for key, value in character_map.items():
def sanitize_text( text, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X' ):
    and lists of strings; non-string entities will be cast to strings.
    if isinstance( text, list ):
        return map( lambda x: sanitize_text( x, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character ), text )
    if not isinstance( text, basestring ):
        text = smart_str( text )
    return _sanitize_text_helper( text, valid_characters=valid_characters, character_map=character_map )
def _sanitize_text_helper( text, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X' ):
        if c in valid_characters:
        elif c in character_map:
            out.append( character_map[c] )
            out.append( invalid_character )  # makes debugging easier
def sanitize_lists_to_string( values, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X'  ):
    if isinstance( values, list ):
        rval = []
        for value in values:
            rval.append( sanitize_lists_to_string( value, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character ) )
        values = ",".join( rval )
    else:
        values = sanitize_text( values, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character )
    return values


def sanitize_param( value, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X' ):
        return sanitize_text( value, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character )
        return map( lambda x: sanitize_text( x, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character ), value )
from galaxy.util.object_wrapper import sanitize_lists_to_string
            # Since we are using an unverified dbkey, we will sanitize the dbkey before use
            chrom_info = os.path.join( self._static_chrom_info_path, "%s.len" % sanitize_lists_to_string( dbkey ) )
"""
Classes for wrapping Objects and Sanitizing string output.
"""

import inspect
import copy_reg
import logging
import string
from numbers import Number
from types import ( NoneType, NotImplementedType, EllipsisType, FunctionType, MethodType, GeneratorType, CodeType,
                    BuiltinFunctionType, BuiltinMethodType, ModuleType, XRangeType, SliceType, TracebackType, FrameType,
                    BufferType, DictProxyType, GetSetDescriptorType, MemberDescriptorType )
from UserDict import UserDict

from galaxy.util import sanitize_lists_to_string as _sanitize_lists_to_string

log = logging.getLogger( __name__ )

# Define different behaviors for different types, see also: https://docs.python.org/2/library/types.html

# Known Callable types
__CALLABLE_TYPES__ = ( FunctionType, MethodType, GeneratorType, CodeType, BuiltinFunctionType, BuiltinMethodType, )

# Always wrap these types without attempting to subclass
__WRAP_NO_SUBCLASS__ =  ( ModuleType, XRangeType, SliceType, BufferType, TracebackType, FrameType, DictProxyType,
                          GetSetDescriptorType, MemberDescriptorType ) + __CALLABLE_TYPES__

# Don't wrap or sanitize.
__DONT_SANITIZE_TYPES__ = ( Number, bool, NoneType, NotImplementedType, EllipsisType, bytearray, )

# Don't wrap, but do sanitize.
__DONT_WRAP_TYPES__ = tuple() #( basestring, ) so that we can get the unsanitized string, we will now wrap basestring instances

# Wrap contents, but not the container
__WRAP_SEQUENCES__ = ( tuple, list, )
__WRAP_SETS__ = ( set, frozenset, )
__WRAP_MAPPINGS__ = ( dict, UserDict, )


# Define the set of characters that are not sanitized, and define a set of mappings for those that are.
# characters that are valid
VALID_CHARACTERS = set( string.letters + string.digits + " -=_.()/+*^,:?!@" )

# characters that are allowed but need to be escaped
CHARACTER_MAP = { '>': '__gt__',
                 '<': '__lt__',
                 "'": '__sq__',
                 '"': '__dq__',
                 '[': '__ob__',
                 ']': '__cb__',
                 '{': '__oc__',
                 '}': '__cc__',
                 '\n': '__cn__',
                 '\r': '__cr__',
                 '\t': '__tc__',
                 '#': '__pd__'}

INVALID_CHARACTER = "X"

def sanitize_lists_to_string( values, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP, invalid_character=INVALID_CHARACTER  ):
    return _sanitize_lists_to_string( values, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character  )


def wrap_with_safe_string( value, no_wrap_classes = None ):
    """
    Recursively wrap values that should be wrapped.
    """

    def __do_wrap( value ):
        if isinstance( value, SafeStringWrapper ):
            # Only ever wrap one-layer
            return value
        if callable( value ):
            safe_class = CallableSafeStringWrapper
        else:
            safe_class = SafeStringWrapper
        if isinstance( value, no_wrap_classes ):
            return value
        if isinstance( value, __DONT_WRAP_TYPES__ ):
            return sanitize_lists_to_string( value, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP )
        if isinstance( value, __WRAP_NO_SUBCLASS__ ):
            return safe_class( value, safe_string_wrapper_function = __do_wrap )
        for this_type in __WRAP_SEQUENCES__ + __WRAP_SETS__:
            if isinstance( value, this_type ):
                return this_type( map( __do_wrap, value ) )
        for this_type in __WRAP_MAPPINGS__:
            if isinstance( value, this_type ):
                # Wrap both key and value
                return this_type( map( lambda x: ( __do_wrap( x[0] ), __do_wrap( x[1] ) ), value.items() ) )
        # Create a dynamic class that joins SafeStringWrapper with the object being wrapped.
        # This allows e.g. isinstance to continue to work.
        try:
            wrapped_class_name = value.__name__
            wrapped_class = value
        except:
            wrapped_class_name = value.__class__.__name__
            wrapped_class = value.__class__
        value_mod = inspect.getmodule( value )
        if value_mod:
            wrapped_class_name = "%s.%s" % ( value_mod.__name__, wrapped_class_name )
        wrapped_class_name = "SafeStringWrapper(%s:%s)" % ( wrapped_class_name, ",".join( sorted( map( str, no_wrap_classes ) ) ) )
        do_wrap_func_name = "__do_wrap_%s" % ( wrapped_class_name )
        do_wrap_func = __do_wrap
        global_dict = globals()
        if wrapped_class_name in global_dict:
            # Check to see if we have created a wrapper for this class yet, if so, reuse
            wrapped_class = global_dict.get( wrapped_class_name )
            do_wrap_func = global_dict.get( do_wrap_func_name, __do_wrap )
        else:
            try:
                wrapped_class = type( wrapped_class_name, ( safe_class, wrapped_class, ), {} )
            except TypeError, e:
                 # Fail-safe for when a class cannot be dynamically subclassed.
                 log.warning( "Unable to create dynamic subclass for %s, %s: %s", type( value), value, e )
                 wrapped_class = type( wrapped_class_name, ( safe_class, ), {} )
            if wrapped_class not in ( SafeStringWrapper, CallableSafeStringWrapper ):
                # Save this wrapper for reuse and pickling/copying
                global_dict[ wrapped_class_name ] = wrapped_class
                do_wrap_func.__name__ = do_wrap_func_name
                global_dict[ do_wrap_func_name ] = do_wrap_func
                def pickle_safe_object( safe_object ):
                    return ( wrapped_class, ( safe_object.unsanitized, do_wrap_func, ) )
                # Set pickle and copy properties
                copy_reg.pickle( wrapped_class, pickle_safe_object, do_wrap_func )
        return wrapped_class( value, safe_string_wrapper_function = do_wrap_func )
    # Determine classes not to wrap
    if no_wrap_classes:
        if not isinstance( no_wrap_classes, ( tuple, list ) ):
            no_wrap_classes = [ no_wrap_classes ]
        no_wrap_classes = list( no_wrap_classes ) + list( __DONT_SANITIZE_TYPES__ ) + [ SafeStringWrapper ]
    else:
        no_wrap_classes = list( __DONT_SANITIZE_TYPES__ ) + [ SafeStringWrapper ]
    no_wrap_classes = tuple( set( sorted( no_wrap_classes, key=str ) ) )
    return __do_wrap( value )


# N.B. refer to e.g. https://docs.python.org/2/reference/datamodel.html for information on Python's Data Model.


class SafeStringWrapper( object ):
    """
    Class that wraps and sanitizes any provided value's attributes 
    that will attempt to be cast into a string.
    
    Attempts to mimic behavior of original class, including operands.
    
    To ensure proper handling of e.g. subclass checks, the *wrap_with_safe_string()*
    method should be used.
    
    This wrapping occurs in a recursive/parasitic fashion, as all called attributes of 
    the originally wrapped object will also be wrapped and sanitized, unless the attribute
    is of a type found in __DONT_SANITIZE_TYPES__ + __DONT_WRAP_TYPES__, where e.g. ~(strings
    will still be sanitized, but not wrapped), and e.g. integers will have neither.
    """
    __UNSANITIZED_ATTRIBUTE_NAME__ = 'unsanitized' 
    __NO_WRAP_NAMES__ = [ '__safe_string_wrapper_function__', __UNSANITIZED_ATTRIBUTE_NAME__]


    def __new__( cls, *arg, **kwd ):
        # We need to define a __new__ since, we are subclassing from e.g. immutable str, which internally sets data 
        # that will be used when other + this (this + other is handled by __add__)
        safe_string_wrapper_function = kwd.get( 'safe_string_wrapper_function', None) or wrap_with_safe_string
        try:
            return super( SafeStringWrapper, cls ).__new__( cls, sanitize_lists_to_string( arg[0], valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP ) )
        except Exception, e:
            log.warning( "Could not provide an argument to %s.__new__: %s; will try without arguments.", cls, e )
            return super( SafeStringWrapper, cls ).__new__( cls )

    def __init__( self, value, safe_string_wrapper_function = wrap_with_safe_string ):
        self.unsanitized = value
        self.__safe_string_wrapper_function__ = safe_string_wrapper_function

    def __str__( self ):
        return sanitize_lists_to_string( self.unsanitized, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP )

    def __repr__( self ):
        return "%s object at %x on: %s" % ( sanitize_lists_to_string( self.__class__.__name__, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP ), id( self ), sanitize_lists_to_string( repr( self.unsanitized ), valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP ) )

    def __lt__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.unsanitized < other

    def __le__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.unsanitized <= other

    def __eq__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.unsanitized == other

    def __ne__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.unsanitized != other

    def __gt__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.unsanitized > other

    def __ge__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.unsanitized >= other

    def __lt__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.unsanitized < other

    def __cmp__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return cmp( self.unsanitized, other )

    # Do not implement __rcmp__, python 2.2 < 2.6

    def __hash__( self ):
        return hash( self.unsanitized )

    def __nonzero__( self ):
        return bool( self.unsanitized )

    # Do not implement __unicode__, we will rely on __str__

    def __getattr__( self, name ):
        if name in SafeStringWrapper.__NO_WRAP_NAMES__:
            #FIXME: is this ever reached?
            return object.__getattr__( self, name )
        return self.__safe_string_wrapper_function__( getattr( self.unsanitized, name ) )

    def __setattr__( self, name, value ):
        if name in SafeStringWrapper.__NO_WRAP_NAMES__:
            return object.__setattr__( self, name, value )
        return setattr( self.unsanitized, name, value )

    def __delattr__( self, name ):
        if name in SafeStringWrapper.__NO_WRAP_NAMES__:
            return object.__delattr__( self, name )
        return delattr( self.unsanitized, name )

    def __getattribute__( self, name ):
        if name in SafeStringWrapper.__NO_WRAP_NAMES__:
            return object.__getattribute__( self, name )
        return self.__safe_string_wrapper_function__( getattr( object.__getattribute__( self, 'unsanitized' ), name ) )

    # Skip Descriptors

    # Skip __slots__
    
    # Don't need __metaclass__, we'll use the helper function to handle with subclassing for e.g. isinstance()

    # Revisit:
    # __instancecheck__
    # __subclasscheck__
    # We are using a helper class to create dynamic subclasses to handle class checks

    # We address __call__ as needed based upon unsanitized, through the use of a CallableSafeStringWrapper class

    def __len__( self ):
        original_value = self.unsanitized
        while isinstance( original_value, SafeStringWrapper ):
            original_value = self.unsanitized
        return len( self.unsanitized )

    def __getitem__( self, key ):
        return self.__safe_string_wrapper_function__( self.unsanitized[ key ] )

    def __setitem__( self, key, value ):
        while isinstance( value, SafeStringWrapper ):
            value = value.unsanitized
        self.unsanitized[ key ] = value

    def __delitem__( self, key ):
        del self.unsanitized[ key ]

    def __iter__( self ):
        return iter( map( self.__safe_string_wrapper_function__, iter( self.unsanitized ) ) )

    # Do not implement __reversed__

    def __contains__( self, item ):
        # FIXME: Do we need to consider if item is/isn't or does/doesn't contain SafeStringWrapper?
        # When considering e.g. nested lists/dicts/etc, this gets complicated
        while isinstance( item, SafeStringWrapper ):
            item = item.unsanitized
        return item in self.unsanitized

    # Not sure that we need these slice methods, but will provide anyway
    def __getslice__( self, i, j ):
        return self.__safe_string_wrapper_function__( self.unsanitized[ i:j ] )

    def __setslice__( self, i, j, value ):
        self.unsanitized[ i:j ] = value

    def __delslice__( self, i, j ):
        del self.unsanitized[ i:j ]

    def __add__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.__safe_string_wrapper_function__( self.unsanitized + other )

    def __sub__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.__safe_string_wrapper_function__( self.unsanitized - other )

    def __mul__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.__safe_string_wrapper_function__( self.unsanitized * other )

    def __floordiv__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.__safe_string_wrapper_function__( self.unsanitized // other )

    def __mod__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.__safe_string_wrapper_function__( self.unsanitized % other )

    def __divmod__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.__safe_string_wrapper_function__( divmod( self.unsanitized, other ) )

    def __pow__( self, *other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.__safe_string_wrapper_function__( pow( self.unsanitized, *other ) )

    def __lshift__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.__safe_string_wrapper_function__( self.unsanitized << other )

    def __rshift__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.__safe_string_wrapper_function__( self.unsanitized >> other )

    def __and__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.__safe_string_wrapper_function__( self.unsanitized & other )

    def __xor__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.__safe_string_wrapper_function__( self.unsanitized ^ other )

    def __or__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.__safe_string_wrapper_function__( self.unsanitized | other )

    def __div__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.__safe_string_wrapper_function__( self.unsanitized / other )

    def __truediv__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.__safe_string_wrapper_function__( self.unsanitized / other )

    # The only reflected operand that we will define is __rpow__, due to coercion rules complications as per docs
    def __rpow__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return self.__safe_string_wrapper_function__( pow( other, self.unsanitized ) )

    # Do not implement in-place operands

    def __neg__( self ):
        return __safe_string_wrapper_function__( -self.unsanitized )

    def __pos__( self ):
        return __safe_string_wrapper_function__( +self.unsanitized )

    def __abs__( self ):
        return __safe_string_wrapper_function__( abs( self.unsanitized ) )

    def __invert__( self ):
        return __safe_string_wrapper_function__( ~self.unsanitized )

    def __complex__( self ):
        return __safe_string_wrapper_function__( complex( self.unsanitized ) )

    def __int__( self ):
        return int( self.unsanitized )

    def __float__( self ):
        return float( self.unsanitized )

    def __oct__( self ):
        return oct( self.unsanitized )

    def __hex__( self ):
        return hex( self.unsanitized )

    def __index__( self ):
        return self.unsanitized.index()

    def __coerce__( self, other ):
        while isinstance( other, SafeStringWrapper ):
            other = other.unsanitized
        return coerce( self.unsanitized, other )

    def __enter__( self ):
        return self.unsanitized.__enter__()

    def __exit__( self, *args ):
        return self.unsanitized.__exit__( *args )

class CallableSafeStringWrapper( SafeStringWrapper ):
    
    def __call__( self, *args, **kwds ):
        return self.__safe_string_wrapper_function__( self.unsanitized( *args, **kwds ) )


# Enable pickling/deepcopy
def pickle_SafeStringWrapper( safe_object ):
    args = ( safe_object.unsanitized, )
    cls = SafeStringWrapper
    if isinstance( safe_object, CallableSafeStringWrapper ):
        cls = CallableSafeStringWrapper
    return ( cls, args )
copy_reg.pickle( SafeStringWrapper, pickle_SafeStringWrapper, wrap_with_safe_string )
copy_reg.pickle( CallableSafeStringWrapper, pickle_SafeStringWrapper, wrap_with_safe_string )