@@ -353,11 +353,13 @@ def test_controlcharacters(self):
353353 testString = "string containing %s" % c
354354 if i >= 32 or c in "\r \n \t " :
355355 # \r, \n and \t are the only legal control chars in XML
356- plistlib .dumps (testString , fmt = plistlib .FMT_XML )
356+ data = plistlib .dumps (testString , fmt = plistlib .FMT_XML )
357+ if c != "\r " :
358+ self .assertEqual (plistlib .loads (data ), testString )
357359 else :
358- self .assertRaises (ValueError ,
359- plistlib .dumps ,
360- testString )
360+ with self .assertRaises (ValueError ):
361+ plistlib .dumps ( testString , fmt = plistlib . FMT_XML )
362+ plistlib . dumps ( testString , fmt = plistlib . FMT_BINARY )
361363
362364 def test_non_bmp_characters (self ):
363365 pl = {'python' : '\U0001f40d ' }
@@ -366,6 +368,14 @@ def test_non_bmp_characters(self):
366368 data = plistlib .dumps (pl , fmt = fmt )
367369 self .assertEqual (plistlib .loads (data ), pl )
368370
371+ def test_lone_surrogates (self ):
372+ for fmt in ALL_FORMATS :
373+ with self .subTest (fmt = fmt ):
374+ with self .assertRaises (UnicodeEncodeError ):
375+ plistlib .dumps ('\ud8ff ' , fmt = fmt )
376+ with self .assertRaises (UnicodeEncodeError ):
377+ plistlib .dumps ('\udcff ' , fmt = fmt )
378+
369379 def test_nondictroot (self ):
370380 for fmt in ALL_FORMATS :
371381 with self .subTest (fmt = fmt ):
@@ -442,6 +452,56 @@ def test_large_timestamp(self):
442452 data = plistlib .dumps (d , fmt = plistlib .FMT_BINARY )
443453 self .assertEqual (plistlib .loads (data ), d )
444454
455+ def test_invalid_binary (self ):
456+ for data in [
457+ # too short data
458+ b'' ,
459+ # too large offset_table_offset and nonstandard offset_size
460+ b'\x00 \x08 '
461+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x03 \x01 '
462+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 '
463+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 '
464+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x2a ' ,
465+ # integer overflow in offset_table_offset
466+ b'\x00 \x08 '
467+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x01 \x01 '
468+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 '
469+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 '
470+ b'\xff \xff \xff \xff \xff \xff \xff \xff ' ,
471+ # offset_size = 0
472+ b'\x00 \x08 '
473+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 '
474+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 '
475+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 '
476+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x09 ' ,
477+ # ref_size = 0
478+ b'\xa1 \x01 \x00 \x08 \x0a '
479+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 '
480+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x02 '
481+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 '
482+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x0b ' ,
483+ # integer overflow in offset
484+ b'\x00 \xff \xff \xff \xff \xff \xff \xff \xff '
485+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x08 \x01 '
486+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 '
487+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 '
488+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x09 ' ,
489+ # invalid ASCII
490+ b'\x51 \xff \x08 '
491+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x01 \x01 '
492+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 '
493+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 '
494+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x0a ' ,
495+ # invalid UTF-16
496+ b'\x61 \xd8 \x00 \x08 '
497+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x01 \x01 '
498+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 '
499+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 '
500+ b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x0b ' ,
501+ ]:
502+ with self .assertRaises (plistlib .InvalidFileException ):
503+ plistlib .loads (b'bplist00' + data , fmt = plistlib .FMT_BINARY )
504+
445505
446506class TestPlistlibDeprecated (unittest .TestCase ):
447507 def test_io_deprecated (self ):
0 commit comments