Hello community, here is the log from the commit of package python-biplist for openSUSE:Factory checked in at 2018-05-23 16:09:18 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-biplist (Old) and /work/SRC/openSUSE:Factory/.python-biplist.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-biplist" Wed May 23 16:09:18 2018 rev:11 rq:611233 version:1.0.3 Changes: -------- --- /work/SRC/openSUSE:Factory/python-biplist/python-biplist.changes 2017-09-04 12:31:31.036051169 +0200 +++ /work/SRC/openSUSE:Factory/.python-biplist.new/python-biplist.changes 2018-05-23 16:09:39.796748386 +0200 @@ -1,0 +2,12 @@ +Tue May 22 12:18:24 UTC 2018 - [email protected] + +- Disable one test that expect int to be equal to long on 32bit + +------------------------------------------------------------------- +Tue May 22 12:13:16 UTC 2018 - [email protected] + +- Update to version 1.0.3: + * Added additional checks to guard against invalid plists. This includes writing and reading plists with recursive collections. + * Handle OverflowErrors when reading dates outside the range of datetime.datetime + +------------------------------------------------------------------- Old: ---- biplist-1.0.2.tar.gz New: ---- biplist-1.0.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-biplist.spec ++++++ --- /var/tmp/diff_new_pack.dufPJ1/_old 2018-05-23 16:09:40.456724216 +0200 +++ /var/tmp/diff_new_pack.dufPJ1/_new 2018-05-23 16:09:40.464723923 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-biplist # -# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -15,29 +15,22 @@ # Please submit bugfixes or comments via http://bugs.opensuse.org/ # -%ifarch x86_64 -%bcond_without test -%else -%bcond_with test -%endif %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-biplist -Version: 1.0.2 +Version: 1.0.3 Release: 0 Summary: A library for reading/writing binary plists License: BSD-3-Clause Group: Development/Languages/Python -Url: https://bitbucket.org/wooster/biplist +URL: https://bitbucket.org/wooster/biplist Source: https://files.pythonhosted.org/packages/source/b/biplist/biplist-%{version}.tar.gz +BuildRequires: %{python_module coverage} BuildRequires: %{python_module devel} +BuildRequires: %{python_module nose} BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros -%if %{with test} -BuildRequires: %{python_module coverage} -BuildRequires: %{python_module nose} -%endif BuildArch: noarch %python_subpackages @@ -58,14 +51,13 @@ %python_install %python_expand %fdupes %{buildroot}%{$python_sitelib} -%if %{with test} %check -%python_exec setup.py test -%endif +# Test on 32bit expects long==int which is true only on py3 +%python_expand nosetests-%{$python_bin_suffix} -e testIntBoundaries %files %{python_files} -%defattr(-,root,root,-) -%doc AUTHORS LICENSE README.md +%license LICENSE +%doc AUTHORS README.md %{python_sitelib}/* %changelog ++++++ biplist-1.0.2.tar.gz -> biplist-1.0.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/biplist-1.0.2/PKG-INFO new/biplist-1.0.3/PKG-INFO --- old/biplist-1.0.2/PKG-INFO 2017-05-11 01:02:57.000000000 +0200 +++ new/biplist-1.0.3/PKG-INFO 2017-12-04 00:36:36.000000000 +0100 @@ -1,12 +1,12 @@ Metadata-Version: 1.1 Name: biplist -Version: 1.0.2 +Version: 1.0.3 Summary: biplist is a library for reading/writing binary plists. Home-page: https://bitbucket.org/wooster/biplist Author: Andrew Wooster Author-email: [email protected] License: BSD -Download-URL: https://bitbucket.org/wooster/biplist/downloads/biplist-1.0.2.tar.gz +Download-URL: https://bitbucket.org/wooster/biplist/downloads/biplist-1.0.3.tar.gz Description: `biplist` is a binary plist parser/generator for Python. Binary Property List (plist) files provide a faster and smaller serialization diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/biplist-1.0.2/biplist/__init__.py new/biplist-1.0.3/biplist/__init__.py --- old/biplist-1.0.2/biplist/__init__.py 2017-05-11 00:53:56.000000000 +0200 +++ new/biplist-1.0.3/biplist/__init__.py 2017-12-04 00:34:32.000000000 +0100 @@ -222,6 +222,8 @@ offsets = None trailer = None currentOffset = 0 + # Used to detect recursive object references. + offsetsStack = [] def __init__(self, fileOrStream): """Raises NotBinaryPlistException.""" @@ -236,6 +238,7 @@ self.contents = '' self.offsets = [] self.currentOffset = 0 + self.offsetsStack = [] def readRoot(self): result = None @@ -250,13 +253,44 @@ trailerContents = self.contents[-32:] try: self.trailer = PlistTrailer._make(unpack("!xxxxxxBBQQQ", trailerContents)) + + if pow(2, self.trailer.offsetSize*8) < self.trailer.offsetTableOffset: + raise InvalidPlistException("Offset size insufficient to reference all objects.") + + if pow(2, self.trailer.objectRefSize*8) < self.trailer.offsetCount: + raise InvalidPlistException("Too many offsets to represent in size of object reference representation.") + offset_size = self.trailer.offsetSize * self.trailer.offsetCount offset = self.trailer.offsetTableOffset + + if offset + offset_size > pow(2, 64): + raise InvalidPlistException("Offset table is excessively long.") + + if self.trailer.offsetSize > 16: + raise InvalidPlistException("Offset size is greater than maximum integer size.") + + if self.trailer.objectRefSize == 0: + raise InvalidPlistException("Object reference size is zero.") + + if offset >= len(self.contents) - 32: + raise InvalidPlistException("Offset table offset is too large.") + + if offset < len("bplist00x"): + raise InvalidPlistException("Offset table offset is too small.") + + if self.trailer.topLevelObjectNumber >= self.trailer.offsetCount: + raise InvalidPlistException("Top level object number is larger than the number of objects.") + offset_contents = self.contents[offset:offset+offset_size] offset_i = 0 + offset_table_length = len(offset_contents) + while offset_i < self.trailer.offsetCount: begin = self.trailer.offsetSize*offset_i - tmp_contents = offset_contents[begin:begin+self.trailer.offsetSize] + end = begin+self.trailer.offsetSize + if end > offset_table_length: + raise InvalidPlistException("End of object is at invalid offset %d in offset table of length %d" % (end, offset_table_length)) + tmp_contents = offset_contents[begin:end] tmp_sized = self.getSizedInteger(tmp_contents, self.trailer.offsetSize) self.offsets.append(tmp_sized) offset_i += 1 @@ -267,11 +301,29 @@ return result def setCurrentOffsetToObjectNumber(self, objectNumber): + if objectNumber > len(self.offsets) - 1: + raise InvalidPlistException("Invalid offset number: %d" % objectNumber) self.currentOffset = self.offsets[objectNumber] + if self.currentOffset in self.offsetsStack: + raise InvalidPlistException("Recursive data structure detected in object: %d" % objectNumber) + + def beginOffsetProtection(self): + self.offsetsStack.append(self.currentOffset) + return self.currentOffset + + def endOffsetProtection(self, offset): + try: + index = self.offsetsStack.index(offset) + self.offsetsStack = self.offsetsStack[:index] + except ValueError as e: + pass def readObject(self): + protection = self.beginOffsetProtection() result = None tmp_byte = self.contents[self.currentOffset:self.currentOffset+1] + if len(tmp_byte) != 1: + raise InvalidPlistException("No object found at offset: %d" % self.currentOffset) marker_byte = unpack("!B", tmp_byte)[0] format = (marker_byte >> 4) & 0x0f extra = marker_byte & 0x0f @@ -279,7 +331,6 @@ def proc_extra(extra): if extra == 0b1111: - #self.currentOffset += 1 extra = self.readObject() return extra @@ -297,11 +348,9 @@ raise InvalidPlistException("Invalid object found at offset: %d" % (self.currentOffset - 1)) # int elif format == 0b0001: - extra = proc_extra(extra) result = self.readInteger(pow(2, extra)) # real elif format == 0b0010: - extra = proc_extra(extra) result = self.readReal(extra) # date elif format == 0b0011 and extra == 0b0011: @@ -335,33 +384,39 @@ result = self.readDict(extra) else: raise InvalidPlistException("Invalid object found: {format: %s, extra: %s}" % (bin(format), bin(extra))) + self.endOffsetProtection(protection) return result + def readContents(self, length, description="Object contents"): + end = self.currentOffset + length + if end >= len(self.contents) - 32: + raise InvalidPlistException("%s extends into trailer" % description) + elif length < 0: + raise InvalidPlistException("%s length is less than zero" % length) + data = self.contents[self.currentOffset:end] + return data + def readInteger(self, byteSize): - result = 0 - original_offset = self.currentOffset - data = self.contents[self.currentOffset:self.currentOffset + byteSize] - result = self.getSizedInteger(data, byteSize, as_number=True) - self.currentOffset = original_offset + byteSize - return result + data = self.readContents(byteSize, "Integer") + self.currentOffset = self.currentOffset + byteSize + return self.getSizedInteger(data, byteSize, as_number=True) def readReal(self, length): - result = 0.0 to_read = pow(2, length) - data = self.contents[self.currentOffset:self.currentOffset+to_read] + data = self.readContents(to_read, "Real") if length == 2: # 4 bytes result = unpack('>f', data)[0] elif length == 3: # 8 bytes result = unpack('>d', data)[0] else: - raise InvalidPlistException("Unknown real of length %d bytes" % to_read) + raise InvalidPlistException("Unknown Real of length %d bytes" % to_read) return result def readRefs(self, count): refs = [] i = 0 while i < count: - fragment = self.contents[self.currentOffset:self.currentOffset+self.trailer.objectRefSize] + fragment = self.readContents(self.trailer.objectRefSize, "Object reference") ref = self.getSizedInteger(fragment, len(fragment)) refs.append(ref) self.currentOffset += self.trailer.objectRefSize @@ -369,6 +424,8 @@ return refs def readArray(self, count): + if not isinstance(count, (int, long)): + raise InvalidPlistException("Count of entries in dict isn't of integer type.") result = [] values = self.readRefs(count) i = 0 @@ -380,6 +437,8 @@ return result def readDict(self, count): + if not isinstance(count, (int, long)): + raise InvalidPlistException("Count of keys/values in dict isn't of integer type.") result = {} keys = self.readRefs(count) values = self.readRefs(count) @@ -394,37 +453,56 @@ return result def readAsciiString(self, length): - result = unpack("!%ds" % length, self.contents[self.currentOffset:self.currentOffset+length])[0] + if not isinstance(length, (int, long)): + raise InvalidPlistException("Length of ASCII string isn't of integer type.") + data = self.readContents(length, "ASCII string") + result = unpack("!%ds" % length, data)[0] self.currentOffset += length return str(result.decode('ascii')) def readUnicode(self, length): + if not isinstance(length, (int, long)): + raise InvalidPlistException("Length of Unicode string isn't of integer type.") actual_length = length*2 - data = self.contents[self.currentOffset:self.currentOffset+actual_length] - # unpack not needed?!! data = unpack(">%ds" % (actual_length), data)[0] + data = self.readContents(actual_length, "Unicode string") self.currentOffset += actual_length return data.decode('utf_16_be') def readDate(self): - result = unpack(">d", self.contents[self.currentOffset:self.currentOffset+8])[0] + data = self.readContents(8, "Date") + x = unpack(">d", data)[0] + if math.isnan(x): + raise InvalidPlistException("Date is NaN") # Use timedelta to workaround time_t size limitation on 32-bit python. - result = datetime.timedelta(seconds=result) + apple_reference_date + try: + result = datetime.timedelta(seconds=x) + apple_reference_date + except OverflowError: + if x > 0: + result = datetime.datetime.max + else: + result = datetime.datetime.min self.currentOffset += 8 return result def readData(self, length): - result = self.contents[self.currentOffset:self.currentOffset+length] + if not isinstance(length, (int, long)): + raise InvalidPlistException("Length of data isn't of integer type.") + result = self.readContents(length, "Data") self.currentOffset += length return Data(result) def readUid(self, length): + if not isinstance(length, (int, long)): + raise InvalidPlistException("Uid length isn't of integer type.") return Uid(self.readInteger(length+1)) def getSizedInteger(self, data, byteSize, as_number=False): """Numbers of 8 bytes are signed integers when they refer to numbers, but unsigned otherwise.""" result = 0 + if byteSize == 0: + raise InvalidPlistException("Encountered integer with byte size of 0.") # 1, 2, and 4 byte integers are unsigned - if byteSize == 1: + elif byteSize == 1: result = unpack('>B', data)[0] elif byteSize == 2: result = unpack('>H', data)[0] @@ -530,6 +608,8 @@ referencePositions = None wrappedTrue = None wrappedFalse = None + # Used to detect recursive object references. + objectsStack = [] def __init__(self, file): self.reset() @@ -548,6 +628,8 @@ # A dict of the positions of the written uniques. self.referencePositions = {} + self.objectsStack = [] + def positionOfObjectReference(self, obj): """If the given object has been written already, return its position in the offset table. Otherwise, return None.""" @@ -588,39 +670,61 @@ output = self.writeOffsetTable(output) output += pack('!xxxxxxBBQQQ', *self.trailer) self.file.write(output) + + def beginRecursionProtection(self, obj): + if not isinstance(obj, (set, dict, list, tuple)): + return + if id(obj) in self.objectsStack: + raise InvalidPlistException("Recursive containers are not allowed in plists.") + self.objectsStack.append(id(obj)) + + def endRecursionProtection(self, obj): + if not isinstance(obj, (set, dict, list, tuple)): + return + try: + index = self.objectsStack.index(id(obj)) + self.objectsStack = self.objectsStack[:index] + except ValueError as e: + pass def wrapRoot(self, root): + result = None + self.beginRecursionProtection(root) + if isinstance(root, bool): if root is True: - return self.wrappedTrue + result = self.wrappedTrue else: - return self.wrappedFalse + result = self.wrappedFalse elif isinstance(root, float): - return FloatWrapper(root) + result = FloatWrapper(root) elif isinstance(root, set): n = set() for value in root: n.add(self.wrapRoot(value)) - return HashableWrapper(n) + result = HashableWrapper(n) elif isinstance(root, dict): n = {} for key, value in iteritems(root): n[self.wrapRoot(key)] = self.wrapRoot(value) - return HashableWrapper(n) + result = HashableWrapper(n) elif isinstance(root, list): n = [] for value in root: n.append(self.wrapRoot(value)) - return HashableWrapper(n) + result = HashableWrapper(n) elif isinstance(root, tuple): n = tuple([self.wrapRoot(value) for value in root]) - return HashableWrapper(n) + result = HashableWrapper(n) elif isinstance(root, (str, unicode)) and not isinstance(root, Data): - return StringWrapper(root) + result = StringWrapper(root) elif isinstance(root, bytes): - return Data(root) + result = Data(root) else: - return root + result = root + + self.endRecursionProtection(root) + return result def incrementByteCount(self, field, incr=1): self.byteCounts = self.byteCounts._replace(**{field:self.byteCounts.__getattribute__(field) + incr}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/biplist-1.0.2/biplist.egg-info/PKG-INFO new/biplist-1.0.3/biplist.egg-info/PKG-INFO --- old/biplist-1.0.2/biplist.egg-info/PKG-INFO 2017-05-11 01:02:57.000000000 +0200 +++ new/biplist-1.0.3/biplist.egg-info/PKG-INFO 2017-12-04 00:36:36.000000000 +0100 @@ -1,12 +1,12 @@ Metadata-Version: 1.1 Name: biplist -Version: 1.0.2 +Version: 1.0.3 Summary: biplist is a library for reading/writing binary plists. Home-page: https://bitbucket.org/wooster/biplist Author: Andrew Wooster Author-email: [email protected] License: BSD -Download-URL: https://bitbucket.org/wooster/biplist/downloads/biplist-1.0.2.tar.gz +Download-URL: https://bitbucket.org/wooster/biplist/downloads/biplist-1.0.3.tar.gz Description: `biplist` is a binary plist parser/generator for Python. Binary Property List (plist) files provide a faster and smaller serialization diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/biplist-1.0.2/biplist.egg-info/SOURCES.txt new/biplist-1.0.3/biplist.egg-info/SOURCES.txt --- old/biplist-1.0.2/biplist.egg-info/SOURCES.txt 2017-05-11 01:02:57.000000000 +0200 +++ new/biplist-1.0.3/biplist.egg-info/SOURCES.txt 2017-12-04 00:36:36.000000000 +0100 @@ -9,6 +9,7 @@ biplist.egg-info/dependency_links.txt biplist.egg-info/not-zip-safe biplist.egg-info/top_level.txt +tests/test_fuzz_results.py tests/test_invalid.py tests/test_utils.py tests/test_valid.py @@ -19,9 +20,24 @@ tests/data/bool_only_binary.plist tests/data/dict_only_binary.plist tests/data/empty_file.plist +tests/data/invalid_object_offset_size.plist +tests/data/invalid_object_ref_size.plist tests/data/large_int_limits.plist tests/data/nskeyedarchiver_example.plist tests/data/simple_binary.plist +tests/data/small_date.plist tests/data/small_real.plist tests/data/unicode_empty.plist -tests/data/unicode_root.plist \ No newline at end of file +tests/data/unicode_root.plist +tests/fuzz_data/array_invalid_count.plist +tests/fuzz_data/ascii_string_negative_length.plist +tests/fuzz_data/ascii_string_too_long.plist +tests/fuzz_data/date_seconds_is_nan.plist +tests/fuzz_data/dictionary_invalid_count.plist +tests/fuzz_data/integer_zero_byte_length.plist +tests/fuzz_data/invalid_object_offset.plist +tests/fuzz_data/invalid_offset_ending.plist +tests/fuzz_data/list_index_out_of_range.plist +tests/fuzz_data/no_marker_byte.plist +tests/fuzz_data/real_invalid_length.plist +tests/fuzz_data/recursive_object_offset.plist \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/biplist-1.0.2/setup.py new/biplist-1.0.3/setup.py --- old/biplist-1.0.2/setup.py 2017-05-11 00:55:28.000000000 +0200 +++ new/biplist-1.0.3/setup.py 2017-12-03 23:18:14.000000000 +0100 @@ -19,7 +19,7 @@ author = 'Andrew Wooster' email = '[email protected]' -version = '1.0.2' +version = '1.0.3' desc = 'biplist is a library for reading/writing binary plists.' setup( Binary files old/biplist-1.0.2/tests/data/invalid_object_offset_size.plist and new/biplist-1.0.3/tests/data/invalid_object_offset_size.plist differ Binary files old/biplist-1.0.2/tests/data/invalid_object_ref_size.plist and new/biplist-1.0.3/tests/data/invalid_object_ref_size.plist differ Binary files old/biplist-1.0.2/tests/data/small_date.plist and new/biplist-1.0.3/tests/data/small_date.plist differ Binary files old/biplist-1.0.2/tests/fuzz_data/array_invalid_count.plist and new/biplist-1.0.3/tests/fuzz_data/array_invalid_count.plist differ Binary files old/biplist-1.0.2/tests/fuzz_data/ascii_string_negative_length.plist and new/biplist-1.0.3/tests/fuzz_data/ascii_string_negative_length.plist differ Binary files old/biplist-1.0.2/tests/fuzz_data/ascii_string_too_long.plist and new/biplist-1.0.3/tests/fuzz_data/ascii_string_too_long.plist differ Binary files old/biplist-1.0.2/tests/fuzz_data/date_seconds_is_nan.plist and new/biplist-1.0.3/tests/fuzz_data/date_seconds_is_nan.plist differ Binary files old/biplist-1.0.2/tests/fuzz_data/dictionary_invalid_count.plist and new/biplist-1.0.3/tests/fuzz_data/dictionary_invalid_count.plist differ Binary files old/biplist-1.0.2/tests/fuzz_data/integer_zero_byte_length.plist and new/biplist-1.0.3/tests/fuzz_data/integer_zero_byte_length.plist differ Binary files old/biplist-1.0.2/tests/fuzz_data/invalid_object_offset.plist and new/biplist-1.0.3/tests/fuzz_data/invalid_object_offset.plist differ Binary files old/biplist-1.0.2/tests/fuzz_data/invalid_offset_ending.plist and new/biplist-1.0.3/tests/fuzz_data/invalid_offset_ending.plist differ Binary files old/biplist-1.0.2/tests/fuzz_data/list_index_out_of_range.plist and new/biplist-1.0.3/tests/fuzz_data/list_index_out_of_range.plist differ Binary files old/biplist-1.0.2/tests/fuzz_data/no_marker_byte.plist and new/biplist-1.0.3/tests/fuzz_data/no_marker_byte.plist differ Binary files old/biplist-1.0.2/tests/fuzz_data/real_invalid_length.plist and new/biplist-1.0.3/tests/fuzz_data/real_invalid_length.plist differ Binary files old/biplist-1.0.2/tests/fuzz_data/recursive_object_offset.plist and new/biplist-1.0.3/tests/fuzz_data/recursive_object_offset.plist differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/biplist-1.0.2/tests/test_fuzz_results.py new/biplist-1.0.3/tests/test_fuzz_results.py --- old/biplist-1.0.2/tests/test_fuzz_results.py 1970-01-01 01:00:00.000000000 +0100 +++ new/biplist-1.0.3/tests/test_fuzz_results.py 2017-12-03 22:36:36.000000000 +0100 @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- + +from biplist import * +import os +from test_utils import * +import unittest + +class TestFuzzResults(unittest.TestCase): + def setUp(self): + pass + + def testCurrentOffsetOutOfRange(self): + try: + readPlist(fuzz_data_path('list_index_out_of_range.plist')) + self.fail("list index out of range, should fail") + except NotBinaryPlistException as e: + pass + except InvalidPlistException as e: + pass + + def testInvalidMarkerByteUnpack(self): + try: + readPlist(fuzz_data_path('no_marker_byte.plist')) + self.fail("No marker byte at object offset") + except NotBinaryPlistException as e: + pass + except InvalidPlistException as e: + pass + + def testInvalidObjectOffset(self): + try: + readPlist(fuzz_data_path('invalid_object_offset.plist')) + self.fail("Invalid object offset in offsets table") + except NotBinaryPlistException as e: + pass + except InvalidPlistException as e: + pass + + def testRecursiveObjectOffset(self): + try: + readPlist(fuzz_data_path('recursive_object_offset.plist')) + self.fail("Recursive object found") + except NotBinaryPlistException as e: + pass + except InvalidPlistException as e: + pass + + def testExcessivelyLongAsciiString(self): + try: + readPlist(fuzz_data_path('ascii_string_too_long.plist')) + self.fail("ASCII string extends into trailer") + except NotBinaryPlistException as e: + pass + except InvalidPlistException as e: + pass + + def testNegativelyLongAsciiString(self): + try: + readPlist(fuzz_data_path('ascii_string_negative_length.plist')) + self.fail("ASCII string length less than zero") + except NotBinaryPlistException as e: + pass + except InvalidPlistException as e: + pass + + def testInvalidOffsetEnding(self): + # The end of the offset is past the end of the offset table. + try: + readPlist(fuzz_data_path('invalid_offset_ending.plist')) + self.fail("End of offset after end of offset table") + except NotBinaryPlistException as e: + pass + except InvalidPlistException as e: + pass + + def testInvalidDictionaryObjectCount(self): + try: + readPlist(fuzz_data_path('dictionary_invalid_count.plist')) + self.fail("Dictionary object count is not of integer type") + except NotBinaryPlistException as e: + pass + except InvalidPlistException as e: + pass + + def testInvalidArrayObjectCount(self): + try: + readPlist(fuzz_data_path('array_invalid_count.plist')) + self.fail("Array object count is not of integer type") + except NotBinaryPlistException as e: + pass + except InvalidPlistException as e: + pass + + def testInvalidRealLength(self): + # We shouldn't have been checking for extra length reals, anyway. + try: + readPlist(fuzz_data_path('real_invalid_length.plist')) + self.fail("Real length is not of integer type") + except NotBinaryPlistException as e: + pass + except InvalidPlistException as e: + pass + + def testNaNDateSeconds(self): + try: + readPlist(fuzz_data_path('date_seconds_is_nan.plist')) + self.fail("Date seconds is NaN") + except NotBinaryPlistException as e: + pass + except InvalidPlistException as e: + pass + + def testIntegerWithZeroByteLength(self): + try: + readPlist(fuzz_data_path('integer_zero_byte_length.plist')) + self.fail("Integer has byte size of 0.") + except NotBinaryPlistException as e: + pass + except InvalidPlistException as e: + pass + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/biplist-1.0.2/tests/test_invalid.py new/biplist-1.0.3/tests/test_invalid.py --- old/biplist-1.0.2/tests/test_invalid.py 2016-06-18 01:44:56.000000000 +0200 +++ new/biplist-1.0.3/tests/test_invalid.py 2017-12-03 02:14:32.000000000 +0100 @@ -1,8 +1,25 @@ +# -*- coding: utf-8 -*- + from biplist import * +from biplist import PlistTrailer +from collections import namedtuple +from math import pow import os +from struct import pack from test_utils import * import unittest +def trailerToString(trailer): + # Trailer is: + # 6 padding bytes + # 1 byte offsetSize + # 1 byte objectRefSize + # 8 bytes offsetCount (number of objects) + # 8 bytes topObjectNumber + # 8 bytes offsetTableOffset + # = 32 byte trailer + return pack('!xxxxxxBBQQQ', *trailer) + class TestInvalidPlistFile(unittest.TestCase): def setUp(self): pass @@ -28,6 +45,129 @@ self.fail("Should not successfully read invalid plist.") except InvalidPlistException as e: pass + + def testInvalidTemplate(self): + # Test that our template plists for range tests are valid. + trailer = PlistTrailer(1, 1, 1, 0, 9) + contents = b''.join([b'bplist00', # header + b'\x08', # bool false + b'\x08', # object at offset 8 + trailerToString(trailer) + ]) + result = readPlistFromString(contents) + self.assertEqual(result, False) + + trailer = PlistTrailer(1, 1, 3, 0, 25) + contents = b''.join([b'bplist00bybiplist1.0', # header + b'\xA2', # array with two entries + b'\x01', # object entry 1 + b'\x02', # object entry 2 + b'\x09', # boolean false + b'\x08', # boolean true + b'\x14', # offset at 20 + b'\x17', # offset at 23 + b'\x18', # offset at 24 + trailerToString(trailer) + ]) + result = readPlistFromString(contents) + self.assertEqual(result, [True, False]) + + def testInvalidOffsetSize(self): + # offset size can't be zero + try: + trailer = PlistTrailer(0, 1, 1, 0, 9) + contents = b''.join([b'bplist00', # header + b'\x08', # bool false + b'\x08', # object at offset 8 + trailerToString(trailer) + ]) + readPlistFromString(contents) + self.fail("Offset size can't be zero") + except InvalidPlistException as e: + pass + + # offset size can't be greater than 16 + try: + trailer = PlistTrailer(17, 1, 1, 0, 9) + contents = b''.join([b'bplist00', # header + b'\x08', # bool false + b'\x08', # object at offset 8 + trailerToString(trailer) + ]) + readPlistFromString(contents) + self.fail("Offset size can't be greater than 16") + except InvalidPlistException as e: + pass + + def testInvalidOffsetOverflow(self): + # The offsets can't overflow the number of bytes used to represent them. + try: + c = readPlist(data_path('invalid_object_offset_size.plist')) + self.fail("Object offset size too small to reference all objects") + except InvalidPlistException as e: + pass + + def testInvalidObjectRefSize(self): + # object reference size can't be zero + try: + trailer = PlistTrailer(1, 0, 1, 0, 9) + contents = b''.join([b'bplist00', # header + b'\x08', # bool false + b'\x08', # object at offset 8 + trailerToString(trailer) + ]) + readPlistFromString(contents) + self.fail("Object reference size can't be zero") + except InvalidPlistException as e: + pass + + def testInvalidObjectRefOverflow(self): + try: + readPlist(data_path('invalid_object_ref_size.plist')) + self.fail("Object ref size too small to reference all objects in the object table") + except InvalidPlistException as e: + pass + + def testInvalidOffsestTableOffset(self): + # offsetTableOffset too large, extending into trailer + try: + trailer = PlistTrailer(1, 1, 1, 0, 10) + contents = b''.join([b'bplist00', # header + b'\x08', # bool false + b'\x08', # object at offset 8 + trailerToString(trailer) + ]) + readPlistFromString(contents) + self.fail("Should not read plist when offsetTableOffset is too large") + except InvalidPlistException as e: + pass + + # offsetTableOffset too small, extending into header or objects + try: + trailer = PlistTrailer(1, 1, 1, 0, 8) + contents = b''.join([b'bplist00', # header + b'\x08', # bool false + b'\x08', # object at offset 8 + trailerToString(trailer) + ]) + readPlistFromString(contents) + self.fail("Should not read plist when offsetTableOffset is too small") + except InvalidPlistException as e: + pass + + def testInvalidTopObjectNumber(self): + # topObjectNumber can't be greater than number of objects + try: + trailer = PlistTrailer(1, 1, 1, 1, 9) + contents = b''.join([b'bplist00', # header + b'\x08', # bool false + b'\x08', # object at offset 8 + trailerToString(trailer) + ]) + readPlistFromString(contents) + self.fail("Top object number should not be greater than number of objects") + except InvalidPlistException as e: + pass if __name__ == '__main__': unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/biplist-1.0.2/tests/test_utils.py new/biplist-1.0.3/tests/test_utils.py --- old/biplist-1.0.2/tests/test_utils.py 2016-06-18 01:44:56.000000000 +0200 +++ new/biplist-1.0.3/tests/test_utils.py 2017-12-03 21:29:16.000000000 +0100 @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + import os import subprocess import sys @@ -5,6 +7,9 @@ def data_path(path): return os.path.join(os.path.dirname(globals()["__file__"]), 'data', path) +def fuzz_data_path(path): + return os.path.join(os.path.dirname(globals()["__file__"]), 'fuzz_data', path) + def run_command(args, verbose = False): """Runs the command and returns the status and the output.""" if verbose: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/biplist-1.0.2/tests/test_valid.py new/biplist-1.0.3/tests/test_valid.py --- old/biplist-1.0.2/tests/test_valid.py 2016-06-18 01:44:56.000000000 +0200 +++ new/biplist-1.0.3/tests/test_valid.py 2017-11-19 01:33:59.000000000 +0100 @@ -57,6 +57,10 @@ result = readPlist(data_path('unicode_empty.plist')) self.assertEqual(result, '') + def testBoolOnly(self): + result = readPlist(data_path('bool_only_binary.plist')) + self.assertEqual(result, False) + def testSmallReal(self): result = readPlist(data_path('small_real.plist')) self.assertEqual(result, {'4 byte real':0.5}) @@ -71,6 +75,11 @@ result = readPlist(data_path("BFPersistentEventInfo.plist")) self.assertEqual(result['lastShownRatePromptDate'], datetime.datetime(1, 12, 30, 0, 0, 0)) + def testSmallDates(self): + result = readPlist(data_path("small_date.plist")) + # Date stored in plist is 0000-12-30T00:00:00Z + self.assertEqual(result, {'MyDate': datetime.datetime(1, 1, 1, 0, 0)}) + def testKeyedArchiverPlist(self): """ Archive is created with class like this: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/biplist-1.0.2/tests/test_write.py new/biplist-1.0.3/tests/test_write.py --- old/biplist-1.0.2/tests/test_write.py 2016-06-18 01:44:56.000000000 +0200 +++ new/biplist-1.0.3/tests/test_write.py 2017-12-04 00:35:04.000000000 +0100 @@ -1,7 +1,12 @@ -#!/usr/local/env python # -*- coding: utf-8 -*- -import datetime, io, os, subprocess, sys, tempfile, unittest +import datetime +import io +import os +import subprocess +import sys +import tempfile +import unittest from biplist import * from biplist import PlistWriter @@ -21,6 +26,8 @@ xrange = range class TestWritePlist(unittest.TestCase): + def setUp(self): + pass def roundTrip(self, case, xml=False, expected=None, reprTest=True): # reprTest may fail randomly if True and the values being encoded include a dictionary with more @@ -332,6 +339,20 @@ self.roundTrip([Uid(1), 1]) self.roundTrip([1, Uid(1)]) self.roundTrip([Uid(1), Uid(1)]) + + def testRecursiveWrite(self): + # Apple libraries disallow recursive containers, so we should fail on + # trying to write those. + root = [] + child = [root] + root.extend(child) + try: + writePlistToString(root) + self.fail("Should not be able to write plists with recursive containers.") + except InvalidPlistException as e: + pass + except: + self.fail("Should get an invalid plist exception for recursive containers.") if __name__ == '__main__': unittest.main()
