1818class ConstructorError (MarkedYAMLError ):
1919 pass
2020
21+
22+ class timezone (datetime .tzinfo ):
23+ def __init__ (self , offset ):
24+ self ._offset = offset
25+ seconds = abs (offset ).total_seconds ()
26+ self ._name = 'UTC%s%02d:%02d' % (
27+ '-' if offset .days < 0 else '+' ,
28+ seconds // 3600 ,
29+ seconds % 3600 // 60
30+ )
31+
32+ def tzname (self , dt = None ):
33+ return self ._name
34+
35+ def utcoffset (self , dt = None ):
36+ return self ._offset
37+
38+ def dst (self , dt = None ):
39+ return datetime .timedelta (0 )
40+
41+ __repr__ = __str__ = tzname
42+
43+
2144class BaseConstructor (object ):
2245
2346 yaml_constructors = {}
@@ -33,6 +56,14 @@ def check_data(self):
3356 # If there are more documents available?
3457 return self .check_node ()
3558
59+ def check_state_key (self , key ):
60+ """Block special attributes/methods from being set in a newly created
61+ object, to prevent user-controlled methods from being called during
62+ deserialization"""
63+ if self .get_state_keys_blacklist_regexp ().match (key ):
64+ raise ConstructorError (None , None ,
65+ "blacklisted key '%s' in instance state found" % (key ,), None )
66+
3667 def get_data (self ):
3768 # Construct and return the next document.
3869 if self .check_node ():
@@ -74,7 +105,7 @@ def construct_object(self, node, deep=False):
74105 constructor = self .yaml_constructors [node .tag ]
75106 else :
76107 for tag_prefix in self .yaml_multi_constructors :
77- if node .tag .startswith (tag_prefix ):
108+ if tag_prefix is not None and node .tag .startswith (tag_prefix ):
78109 tag_suffix = node .tag [len (tag_prefix ):]
79110 constructor = self .yaml_multi_constructors [tag_prefix ]
80111 break
@@ -293,7 +324,7 @@ def construct_yaml_binary(self, node):
293324 return str (value ).decode ('base64' )
294325 except (binascii .Error , UnicodeEncodeError ), exc :
295326 raise ConstructorError (None , None ,
296- "failed to decode base64 data: %s" % exc , node .start_mark )
327+ "failed to decode base64 data: %s" % exc , node .start_mark )
297328
298329 timestamp_regexp = re .compile (
299330 ur'''^(?P<year>[0-9][0-9][0-9][0-9])
@@ -320,22 +351,23 @@ def construct_yaml_timestamp(self, node):
320351 minute = int (values ['minute' ])
321352 second = int (values ['second' ])
322353 fraction = 0
354+ tzinfo = None
323355 if values ['fraction' ]:
324356 fraction = values ['fraction' ][:6 ]
325357 while len (fraction ) < 6 :
326358 fraction += '0'
327359 fraction = int (fraction )
328- delta = None
329360 if values ['tz_sign' ]:
330361 tz_hour = int (values ['tz_hour' ])
331362 tz_minute = int (values ['tz_minute' ] or 0 )
332363 delta = datetime .timedelta (hours = tz_hour , minutes = tz_minute )
333364 if values ['tz_sign' ] == '-' :
334365 delta = - delta
335- data = datetime .datetime (year , month , day , hour , minute , second , fraction )
336- if delta :
337- data -= delta
338- return data
366+ tzinfo = timezone (delta )
367+ elif values ['tz' ]:
368+ tzinfo = timezone (datetime .timedelta (0 ))
369+ return datetime .datetime (year , month , day , hour , minute , second , fraction ,
370+ tzinfo = tzinfo )
339371
340372 def construct_yaml_omap (self , node ):
341373 # Note: we do not check for duplicate keys, because it's too
@@ -471,6 +503,16 @@ def construct_undefined(self, node):
471503 SafeConstructor .construct_undefined )
472504
473505class FullConstructor (SafeConstructor ):
506+ # 'extend' is blacklisted because it is used by
507+ # construct_python_object_apply to add `listitems` to a newly generate
508+ # python instance
509+ def get_state_keys_blacklist (self ):
510+ return ['^extend$' , '^__.*__$' ]
511+
512+ def get_state_keys_blacklist_regexp (self ):
513+ if not hasattr (self , 'state_keys_blacklist_regexp' ):
514+ self .state_keys_blacklist_regexp = re .compile ('(' + '|' .join (self .get_state_keys_blacklist ()) + ')' )
515+ return self .state_keys_blacklist_regexp
474516
475517 def construct_python_str (self , node ):
476518 return self .construct_scalar (node ).encode ('utf-8' )
@@ -497,7 +539,7 @@ def find_python_module(self, name, mark, unsafe=False):
497539 except ImportError , exc :
498540 raise ConstructorError ("while constructing a Python module" , mark ,
499541 "cannot find module %r (%s)" % (name .encode ('utf-8' ), exc ), mark )
500- if not name in sys .modules :
542+ if name not in sys .modules :
501543 raise ConstructorError ("while constructing a Python module" , mark ,
502544 "module %r is not imported" % name .encode ('utf-8' ), mark )
503545 return sys .modules [name ]
@@ -517,7 +559,7 @@ def find_python_name(self, name, mark, unsafe=False):
517559 except ImportError , exc :
518560 raise ConstructorError ("while constructing a Python object" , mark ,
519561 "cannot find module %r (%s)" % (module_name .encode ('utf-8' ), exc ), mark )
520- if not module_name in sys .modules :
562+ if module_name not in sys .modules :
521563 raise ConstructorError ("while constructing a Python object" , mark ,
522564 "module %r is not imported" % module_name .encode ('utf-8' ), mark )
523565 module = sys .modules [module_name ]
@@ -566,19 +608,24 @@ def make_python_instance(self, suffix, node,
566608 else :
567609 return cls (* args , ** kwds )
568610
569- def set_python_instance_state (self , instance , state ):
611+ def set_python_instance_state (self , instance , state , unsafe = False ):
570612 if hasattr (instance , '__setstate__' ):
571613 instance .__setstate__ (state )
572614 else :
573615 slotstate = {}
574616 if isinstance (state , tuple ) and len (state ) == 2 :
575617 state , slotstate = state
576618 if hasattr (instance , '__dict__' ):
619+ if not unsafe and state :
620+ for key in state .keys ():
621+ self .check_state_key (key )
577622 instance .__dict__ .update (state )
578623 elif state :
579624 slotstate .update (state )
580625 for key , value in slotstate .items ():
581- setattr (object , key , value )
626+ if not unsafe :
627+ self .check_state_key (key )
628+ setattr (instance , key , value )
582629
583630 def construct_python_object (self , suffix , node ):
584631 # Format:
@@ -699,6 +746,10 @@ def make_python_instance(self, suffix, node, args=None, kwds=None, newobj=False)
699746 return super (UnsafeConstructor , self ).make_python_instance (
700747 suffix , node , args , kwds , newobj , unsafe = True )
701748
749+ def set_python_instance_state (self , instance , state ):
750+ return super (UnsafeConstructor , self ).set_python_instance_state (
751+ instance , state , unsafe = True )
752+
702753UnsafeConstructor .add_multi_constructor (
703754 u'tag:yaml.org,2002:python/object/apply:' ,
704755 UnsafeConstructor .construct_python_object_apply )
0 commit comments