@ -46,6 +46,7 @@ import logging
import os
import os
import pickle as py_pickle
import pickle as py_pickle
import pstats
import pstats
import re
import signal
import signal
import socket
import socket
import struct
import struct
@ -769,6 +770,45 @@ else:
_Unpickler = pickle . Unpickler
_Unpickler = pickle . Unpickler
#: A list of compiled regex patterns which allow end-users to selectively opt
#: into deserializing certain globals.
_PICKLE_GLOBAL_WHITELIST_PATTERNS = None
_PICKLE_GLOBAL_WHITELIST = None
def set_pickle_whitelist ( pattern_strings ) :
"""
Specify regex patterns that control allowable global unpickling functions .
` pattern_strings ` is sequence of pattern strings that will be fed into
` re . compile ` and then used to authenticate pickle calls . In order for a
non - trivially typed message to unpickle , one of these patterns must
match against a complete [ module ] . [ function ] string .
"""
if not isinstance ( pattern_strings , ( tuple , list , set ) ) :
pattern_strings = ( pattern_strings , )
global _PICKLE_GLOBAL_WHITELIST
global _PICKLE_GLOBAL_WHITELIST_PATTERNS
_PICKLE_GLOBAL_WHITELIST = pattern_strings
_PICKLE_GLOBAL_WHITELIST_PATTERNS = [ ]
for patt_str in pattern_strings :
if not patt_str . endswith ( ' $ ' ) :
patt_str + = ' $ '
_PICKLE_GLOBAL_WHITELIST_PATTERNS . append ( re . compile ( patt_str ) )
def _test_pickle_whitelist_accept ( module , func ) :
if not _PICKLE_GLOBAL_WHITELIST_PATTERNS :
return False
test_str = " {} . {} " . format ( module , func )
return bool ( any (
patt . match ( test_str ) for patt in _PICKLE_GLOBAL_WHITELIST_PATTERNS ) )
class Message ( object ) :
class Message ( object ) :
"""
"""
Messages are the fundamental unit of communication , comprising fields from
Messages are the fundamental unit of communication , comprising fields from
@ -868,7 +908,14 @@ class Message(object):
return BytesType
return BytesType
elif SimpleNamespace and module == ' types ' and func == ' SimpleNamespace ' :
elif SimpleNamespace and module == ' types ' and func == ' SimpleNamespace ' :
return SimpleNamespace
return SimpleNamespace
raise StreamError ( ' cannot unpickle %r / %r ' , module , func )
elif _test_pickle_whitelist_accept ( module , func ) :
try :
return getattr ( import_module ( module ) , func )
except AttributeError as e :
LOG . info ( str ( e ) )
raise StreamError (
' cannot unpickle %r / %r - try using `set_pickle_whitelist` ' ,
module , func )
@property
@property
def is_dead ( self ) :
def is_dead ( self ) :
@ -968,8 +1015,8 @@ class Message(object):
# Must occur off the broker thread.
# Must occur off the broker thread.
try :
try :
obj = unpickler . load ( )
obj = unpickler . load ( )
except :
except Exception as e :
LOG . error ( ' raw pickle was: %r ', self . data )
LOG . error ( ' raw pickle was: %r (exc: %r ) ', self . data , e )
raise
raise
self . _unpickled = obj
self . _unpickled = obj
except ( TypeError , ValueError ) :
except ( TypeError , ValueError ) :
@ -3845,6 +3892,9 @@ class ExternalContext(object):
Router . max_message_size = self . config [ ' max_message_size ' ]
Router . max_message_size = self . config [ ' max_message_size ' ]
if self . config [ ' profiling ' ] :
if self . config [ ' profiling ' ] :
enable_profiling ( )
enable_profiling ( )
if self . config [ ' pickle_whitelist_patterns ' ] :
set_pickle_whitelist ( self . config [ ' pickle_whitelist_patterns ' ] )
self . broker = Broker ( activate_compat = False )
self . broker = Broker ( activate_compat = False )
self . router = Router ( self . broker )
self . router = Router ( self . broker )
self . router . debug = self . config . get ( ' debug ' , False )
self . router . debug = self . config . get ( ' debug ' , False )