CVE-2015-10062: Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection') (CWE-74)
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.
CWE description: 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.
CVSS v3 base score: 9.8
Published: 2023-01-17T19:15Z
Repository: https://github.com/blankenberg/galaxy-data-resource
Language: Python
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 date: 2015-01-13 10:27:49 -0500
Commit message: Fix a critical security vulnerability where unsanitized user-modifiable values could be included in a command line template.

Diff: diff --git a/lib/galaxy/datatypes/metadata.py b/lib/galaxy/datatypes/metadata.py
index b972e214f..8d177c02e 100644
--- a/lib/galaxy/datatypes/metadata.py
+++ b/lib/galaxy/datatypes/metadata.py
@@ -18,10 +18,11 @@ from galaxy import eggs
 eggs.require( "SQLAlchemy >= 0.4" )
 from sqlalchemy.orm import object_session
 
 import galaxy.model
 from galaxy.util import listify
+from galaxy.util.object_wrapper import sanitize_lists_to_string
 from galaxy.util import stringify_dictionary_keys
 from galaxy.util import string_as_bool
 from galaxy.util import in_directory
 from galaxy.util.odict import odict
 from galaxy.web import form_builder
@@ -230,10 +231,13 @@ class MetadataParameter( object ):
             return self.get_html_field( value=value, context=context, other_values=other_values, **kwd ).get_html()
 
     def to_string( self, value ):
         return str( value )
 
+    def to_safe_string( self, value ):
+        return sanitize_lists_to_string( self.to_string( value ) )
+
     def make_copy( self, value, target_context = None, source_context = None ):
         return copy.deepcopy( value )
 
     @classmethod
     def marshal ( cls, value ):
@@ -478,10 +482,14 @@ class ListParameter( MetadataParameter ):
 class DictParameter( MetadataParameter ):
 
     def to_string( self, value ):
         return  json.dumps( value )
 
+    def to_safe_string( self, value ):
+        # We do not sanitize json dicts
+        return json.safe_dumps( value )
+
 
 class PythonObjectParameter( MetadataParameter ):
 
     def to_string( self, value ):
         if not value:
@@ -508,10 +516,14 @@ class FileParameter( MetadataParameter ):
     def to_string( self, value ):
         if not value:
             return str( self.spec.no_value )
         return value.file_name
 
+    def to_safe_string( self, value ):
+        # We do not sanitize file names
+        return self.to_string( value )
+
     def get_html_field( self, value=None, context=None, other_values=None, **kwd ):
         context = context or {}
         other_values = other_values or {}
         return form_builder.TextField( self.spec.name, value=str( value.id ) )
 
diff --git a/lib/galaxy/tools/evaluation.py b/lib/galaxy/tools/evaluation.py
index 6c38e8325..16720a799 100644
--- a/lib/galaxy/tools/evaluation.py
+++ b/lib/galaxy/tools/evaluation.py
@@ -1,13 +1,15 @@
 import os
 import tempfile
 
 from galaxy import model
+from galaxy.util.object_wrapper import wrap_with_safe_string
 from galaxy.util.bunch import Bunch
 from galaxy.util.none_like import NoneDataset
 from galaxy.util.template import fill_template
 from galaxy.tools.wrappers import (
+    ToolParameterValueWrapper,
     DatasetFilenameWrapper,
     DatasetListWrapper,
     DatasetCollectionWrapper,
     LibraryDatasetValueWrapper,
     SelectToolParameterWrapper,
@@ -112,10 +114,13 @@ class ToolEvaluator( object ):
         input_dataset_paths = dataset_path_rewrites( input_paths )
         self.__populate_wrappers(param_dict, input_dataset_paths)
         self.__populate_input_dataset_wrappers(param_dict, input_datasets, input_dataset_paths)
         self.__populate_output_dataset_wrappers(param_dict, output_datasets, output_paths, job_working_directory)
         self.__populate_unstructured_path_rewrites(param_dict)
+        # 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
         self.__populate_non_job_params(param_dict)
 
         # Return the dictionary of parameters
         return param_dict
 
@@ -332,10 +337,28 @@ class ToolEvaluator( object ):
         if not self.tool.check_values and self.unstructured_path_rewriter:
             # The tools weren't "wrapped" yet, but need to be in order to get
             #the paths rewritten.
             self.__walk_inputs( self.tool.inputs, param_dict, rewrite_unstructured_paths )
 
+    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 )
+
     def build( self ):
         """
         Build runtime description of job to execute, evaluate command and
         config templates corresponding to this tool with these inputs on this
         compute environment.
diff --git a/lib/galaxy/tools/wrappers.py b/lib/galaxy/tools/wrappers.py
index faf63aae7..4a53f4ae8 100644
--- a/lib/galaxy/tools/wrappers.py
+++ b/lib/galaxy/tools/wrappers.py
@@ -1,9 +1,10 @@
 import pipes
 from galaxy import exceptions
 from galaxy.util.none_like import NoneDataset
 from galaxy.util import odict
+from galaxy.util.object_wrapper import wrap_with_safe_string
 
 from logging import getLogger
 log = getLogger( __name__ )
 
 # Fields in .log files corresponding to paths, must have one of the following
@@ -160,14 +161,17 @@ class DatasetFilenameWrapper( ToolParameterValueWrapper ):
         def __getattr__( self, name ):
             rval = self.metadata.get( name, None )
             if name in self.metadata.spec:
                 if rval is None:
                     rval = self.metadata.spec[name].no_value
-                rval = self.metadata.spec[name].param.to_string( rval )
+                rval = self.metadata.spec[ name ].param.to_safe_string( rval )
                 # Store this value, so we don't need to recalculate if needed
                 # again
                 setattr( self, name, rval )
+            else:
+                #escape string value of non-defined metadata value
+                rval = wrap_with_safe_string( rval )
             return rval
 
         def __nonzero__( self ):
             return self.metadata.__nonzero__()
 
@@ -188,13 +192,17 @@ class DatasetFilenameWrapper( ToolParameterValueWrapper ):
             try:
                 # TODO: allow this to work when working with grouping
                 ext = tool.inputs[name].extensions[0]
             except:
                 ext = 'data'
-            self.dataset = NoneDataset( datatypes_registry=datatypes_registry, ext=ext )
+            self.dataset = wrap_with_safe_string( NoneDataset( datatypes_registry=datatypes_registry, ext=ext ), no_wrap_classes=ToolParameterValueWrapper )
         else:
-            self.dataset = dataset
+            # 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 )
             self.metadata = self.MetadataWrapper( dataset.metadata )
         self.datatypes_registry = datatypes_registry
         self.false_path = getattr( dataset_path, "false_path", None )
         self.false_extra_files_path = getattr( dataset_path, "false_extra_files_path", None )
 
@@ -208,11 +216,11 @@ class DatasetFilenameWrapper( ToolParameterValueWrapper ):
 
     def __str__( self ):
         if self.false_path is not None:
             return self.false_path
         else:
-            return self.dataset.file_name
+            return self.unsanitized.file_name
 
     def __getattr__( self, key ):
         if self.false_path is not None and key == 'file_name':
             # Path to dataset was rewritten for this job.
             return self.false_path
@@ -228,11 +236,11 @@ class DatasetFilenameWrapper( ToolParameterValueWrapper ):
             except AttributeError:
                 # Otherwise, we have an input - delegate to model and
                 # object store to find the static location of this
                 # directory.
                 try:
-                    return self.dataset.extra_files_path
+                    return self.unsanitized.extra_files_path
                 except exceptions.ObjectNotFound:
                     # NestedObjectstore raises an error here
                     # instead of just returning a non-existent
                     # path like DiskObjectStore.
                     raise
diff --git a/lib/galaxy/util/__init__.py b/lib/galaxy/util/__init__.py
index 6e957f2b0..bcf9579e7 100644
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -358,50 +358,61 @@ mapped_chars = { '>': '__gt__',
                  '\r': '__cr__',
                  '\t': '__tc__',
                  '#': '__pd__'}
 
 
-def restore_text(text):
+def restore_text( text, character_map=mapped_chars ):
     """Restores sanitized text"""
     if not text:
         return text
-    for key, value in mapped_chars.items():
+    for key, value in character_map.items():
         text = text.replace(value, key)
     return text
 
 
-def sanitize_text(text):
+def sanitize_text( text, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X' ):
     """
     Restricts the characters that are allowed in text; accepts both strings
-    and lists of strings.
+    and lists of strings; non-string entities will be cast to strings.
     """
-    if isinstance( text, basestring ):
-        return _sanitize_text_helper(text)
-    elif isinstance( text, list ):
-        return [ _sanitize_text_helper(t) for t in text ]
+    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):
+def _sanitize_text_helper( text, valid_characters=valid_chars, character_map=mapped_chars, invalid_character='X' ):
     """Restricts the characters that are allowed in a string"""
 
     out = []
     for c in text:
-        if c in valid_chars:
+        if c in valid_characters:
             out.append(c)
-        elif c in mapped_chars:
-            out.append(mapped_chars[c])
+        elif c in character_map:
+            out.append( character_map[c] )
         else:
-            out.append('X')  # makes debugging easier
+            out.append( invalid_character )  # makes debugging easier
     return ''.join(out)
 
 
-def sanitize_param(value):
+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' ):
     """Clean incoming parameters (strings or lists)"""
     if isinstance( value, basestring ):
-        return sanitize_text(value)
+        return sanitize_text( value, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character )
     elif isinstance( value, list ):
-        return map(sanitize_text, value)
+        return map( lambda x: sanitize_text( x, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character ), value )
     else:
         raise Exception('Unknown parameter type (%s)' % ( type( value ) ))
 
 valid_filename_chars = set( string.ascii_letters + string.digits + '_.' )
 invalid_filenames = [ '', '.', '..' ]
diff --git a/lib/galaxy/util/dbkeys.py b/lib/galaxy/util/dbkeys.py
index 2c36902b8..9bf7d09e8 100644
--- a/lib/galaxy/util/dbkeys.py
+++ b/lib/galaxy/util/dbkeys.py
@@ -2,10 +2,11 @@
 Functionality for dealing with dbkeys.
 """
 #dbkeys read from disk using builds.txt
 from galaxy.util import read_dbnames
 from galaxy.util.json import loads
+from galaxy.util.object_wrapper import sanitize_lists_to_string
 import os.path
 
 
 class GenomeBuilds( object ):
     default_value = "?"
@@ -82,8 +83,9 @@ class GenomeBuilds( object ):
             if dbkey_table is not None:
                 chrom_info = dbkey_table.get_entry( 'value', dbkey, 'len_path', default=None )
         # use configured server len path
         if not chrom_info:
             # Default to built-in build.
-            chrom_info = os.path.join( self._static_chrom_info_path, "%s.len" % dbkey )
+            # 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 ) )
         chrom_info = os.path.abspath( chrom_info )
         return ( chrom_info, db_dataset )
diff --git a/lib/galaxy/util/object_wrapper.py b/lib/galaxy/util/object_wrapper.py
new file mode 100644
index 000000000..a620802ca
--- /dev/null
+++ b/lib/galaxy/util/object_wrapper.py
@@ -0,0 +1,436 @@
+"""
+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 )
+

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 )