mirror of
https://github.com/python/cpython.git
synced 2024-11-27 03:45:08 +08:00
Issue #985064: Make plistlib more resilient to faulty input plists.
Patch by Mher Movsisyan.
This commit is contained in:
commit
32b5cb0a66
@ -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):
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user