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