diff options
author | Ned Deily <nad@acm.org> | 2011-05-28 10:02:30 (GMT) |
---|---|---|
committer | Ned Deily <nad@acm.org> | 2011-05-28 10:02:30 (GMT) |
commit | 32b5cb0a66956785275c62e1d20b6e5ee4241079 (patch) | |
tree | 2c830d489ec2d9fc76c930089a119fe7744a7824 | |
parent | 056f5b9dadd834c26fc6deb87a831b6a3d9f4bf7 (diff) | |
parent | b8e59f77e65ba4caeda8d910bd66df01a468cbea (diff) | |
download | cpython-32b5cb0a66956785275c62e1d20b6e5ee4241079.zip cpython-32b5cb0a66956785275c62e1d20b6e5ee4241079.tar.gz cpython-32b5cb0a66956785275c62e1d20b6e5ee4241079.tar.bz2 |
Issue #985064: Make plistlib more resilient to faulty input plists.
Patch by Mher Movsisyan.
-rw-r--r-- | Lib/plistlib.py | 59 | ||||
-rw-r--r-- | Lib/test/test_plistlib.py | 26 | ||||
-rw-r--r-- | Misc/ACKS | 1 | ||||
-rw-r--r-- | Misc/NEWS | 3 |
4 files changed, 67 insertions, 22 deletions
diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 82d456a..2e7e512 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -68,13 +68,15 @@ def readPlist(pathOrFile): usually is a dictionary). """ didOpen = False - if isinstance(pathOrFile, str): - pathOrFile = open(pathOrFile, 'rb') - didOpen = True - p = PlistParser() - rootObject = p.parse(pathOrFile) - if didOpen: - pathOrFile.close() + try: + if isinstance(pathOrFile, str): + pathOrFile = open(pathOrFile, 'rb') + didOpen = True + p = PlistParser() + rootObject = p.parse(pathOrFile) + finally: + if didOpen: + pathOrFile.close() return rootObject @@ -83,15 +85,17 @@ def writePlist(rootObject, pathOrFile): file name or a (writable) file object. """ didOpen = False - if isinstance(pathOrFile, str): - pathOrFile = open(pathOrFile, 'wb') - didOpen = True - writer = PlistWriter(pathOrFile) - writer.writeln("<plist version=\"1.0\">") - writer.writeValue(rootObject) - writer.writeln("</plist>") - if didOpen: - pathOrFile.close() + try: + if isinstance(pathOrFile, str): + pathOrFile = open(pathOrFile, 'wb') + didOpen = True + writer = PlistWriter(pathOrFile) + writer.writeln("<plist version=\"1.0\">") + writer.writeValue(rootObject) + writer.writeln("</plist>") + finally: + if didOpen: + pathOrFile.close() def readPlistFromBytes(data): @@ -352,7 +356,6 @@ class Data: def __repr__(self): return "%s(%s)" % (self.__class__.__name__, repr(self.data)) - class PlistParser: def __init__(self): @@ -362,11 +365,11 @@ class PlistParser: def parse(self, fileobj): from xml.parsers.expat import ParserCreate - parser = ParserCreate() - parser.StartElementHandler = self.handleBeginElement - parser.EndElementHandler = self.handleEndElement - parser.CharacterDataHandler = self.handleData - parser.ParseFile(fileobj) + self.parser = ParserCreate() + self.parser.StartElementHandler = self.handleBeginElement + self.parser.EndElementHandler = self.handleEndElement + self.parser.CharacterDataHandler = self.handleData + self.parser.ParseFile(fileobj) return self.root def handleBeginElement(self, element, attrs): @@ -385,12 +388,18 @@ class PlistParser: def addObject(self, value): if self.currentKey is not None: + if not isinstance(self.stack[-1], type({})): + raise ValueError("unexpected element at line %d" % + self.parser.CurrentLineNumber) self.stack[-1][self.currentKey] = value self.currentKey = None elif not self.stack: # this is the root object self.root = value else: + if not isinstance(self.stack[-1], type([])): + raise ValueError("unexpected element at line %d" % + self.parser.CurrentLineNumber) self.stack[-1].append(value) def getData(self): @@ -405,9 +414,15 @@ class PlistParser: self.addObject(d) self.stack.append(d) def end_dict(self): + if self.currentKey: + raise ValueError("missing value for key '%s' at line %d" % + (self.currentKey,self.parser.CurrentLineNumber)) self.stack.pop() def end_key(self): + if self.currentKey or not isinstance(self.stack[-1], type({})): + raise ValueError("unexpected key at line %d" % + self.parser.CurrentLineNumber) self.currentKey = self.getData() def begin_array(self, attrs): diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index dfa4725..5f980d0 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -175,6 +175,32 @@ class TestPlistlib(unittest.TestCase): self.assertEqual(test1, result1) self.assertEqual(test2, result2) + def test_invalidarray(self): + for i in ["<key>key inside an array</key>", + "<key>key inside an array2</key><real>3</real>", + "<true/><key>key inside an array3</key>"]: + self.assertRaises(ValueError, plistlib.readPlistFromBytes, + ("<plist><array>%s</array></plist>"%i).encode()) + + def test_invaliddict(self): + for i in ["<key><true/>k</key><string>compound key</string>", + "<key>single key</key>", + "<string>missing key</string>", + "<key>k1</key><string>v1</string><real>5.3</real>" + "<key>k1</key><key>k2</key><string>double key</string>"]: + self.assertRaises(ValueError, plistlib.readPlistFromBytes, + ("<plist><dict>%s</dict></plist>"%i).encode()) + self.assertRaises(ValueError, plistlib.readPlistFromBytes, + ("<plist><array><dict>%s</dict></array></plist>"%i).encode()) + + def test_invalidinteger(self): + self.assertRaises(ValueError, plistlib.readPlistFromBytes, + b"<plist><integer>not integer</integer></plist>") + + def test_invalidreal(self): + self.assertRaises(ValueError, plistlib.readPlistFromBytes, + b"<plist><integer>not real</integer></plist>") + def test_main(): support.run_unittest(TestPlistlib) @@ -608,6 +608,7 @@ Paul Moore Derek Morr James A Morrison Pablo Mouzo +Mher Movsisyan Sjoerd Mullender Sape Mullender Michael Muller @@ -18,6 +18,9 @@ Core and Builtins Library ------- +- Issue #985064: Make plistlib more resilient to faulty input plists. + Patch by Mher Movsisyan. + - Issue #12175: RawIOBase.readall() now returns None if read() returns None. - Issue #12175: FileIO.readall() now raises a ValueError instead of an IOError |