diff -r 85c04fdaa404 Lib/test/test_xmlrpc.py
--- a/Lib/test/test_xmlrpc.py Wed May 22 15:28:30 2013 +0300
+++ b/Lib/test/test_xmlrpc.py Wed May 22 21:00:22 2013 +0300
@@ -187,6 +187,26 @@
self.assertEqual(s, "abc \xc2\x95")
self.assertEqual(items, [("def \xc2\x96", "ghi \xc2\x97")])
+ def test_dump_invalid_string(self):
+ # ASCII control characters
+ for i in set(range(32)) - {9, 10, 13}:
+ self.assertRaises(ValueError, xmlrpclib.dumps, (chr(i),))
+ # UTF-8 encoded surrogates, U+FFFE, and U+FFFF
+ for s in ('\xed\xb0\x80', '\xed\xbf\xbf',
+ '\xef\xbf\xbe', '\xef\xbf\xbf'):
+ self.assertRaises(ValueError, xmlrpclib.dumps, (s,))
+ # Invalid UTF-8
+ for s in ('\x80', '\xc1\xbf', '\xc2', '\xe0\x9f\xbf', '\xe0\xa0',
+ '\xf0\x8f\xbf\xbf', '\xf0\x90\x80', '\xf4\x90\x80\x80'):
+ self.assertRaises(ValueError, xmlrpclib.dumps, (s,))
+
+ @unittest.skipUnless(have_unicode, 'requires unicode support')
+ def test_dump_invalid_unicode(self):
+ for i in ((set(range(32)) - {9, 10, 13}) |
+ set(range(0xdc00, 0xc000)) |
+ {0xfffe, 0xffff}):
+ self.assertRaises(ValueError, xmlrpclib.dumps, (unichr(i),))
+
class HelperTestCase(unittest.TestCase):
def test_escape(self):
diff -r 85c04fdaa404 Lib/xmlrpclib.py
--- a/Lib/xmlrpclib.py Wed May 22 15:28:30 2013 +0300
+++ b/Lib/xmlrpclib.py Wed May 22 21:00:22 2013 +0300
@@ -142,6 +142,7 @@
import socket
import errno
import httplib
+import re
try:
import gzip
except ImportError:
@@ -171,6 +172,20 @@
data = unicode(data, encoding)
return data
+if unicode:
+ _invalid_chars_re = re.compile(u'[\x00-\x08\x0b\x0c\x0e-\x1f'
+ u'\udc00-\udfff|\ufffe|\uffff]', re.S)
+ def _check_unicode(s, isinvalid=_invalid_chars_re.search):
+ if isinvalid(s):
+ raise ValueError('invalid string')
+ def _check_str(s, encoding, _check_unicode=_check_unicode):
+ _check_unicode(unicode(s, encoding or 'utf-8'))
+else:
+ _invalid_chars_re = re.compile('[\x00-\x08\x0b\x0c\x0e-\x1f', re.S)
+ def _check_str(s, encoding, isinvalid=_invalid_chars_re.search):
+ if isinvalid(s):
+ raise ValueError('invalid string')
+
def escape(s, replace=string.replace):
s = replace(s, "&", "&")
s = replace(s, "<", "<")
@@ -690,6 +705,7 @@
dispatch[FloatType] = dump_double
def dump_string(self, value, write, escape=escape):
+ _check_str(value, self.encoding)
write("")
write(escape(value))
write("\n")
@@ -697,6 +713,7 @@
if unicode:
def dump_unicode(self, value, write, escape=escape):
+ _check_unicode(value)
value = value.encode(self.encoding)
write("")
write(escape(value))
@@ -726,11 +743,13 @@
write("\n")
for k, v in value.items():
write("\n")
- if type(k) is not StringType:
- if unicode and type(k) is UnicodeType:
- k = k.encode(self.encoding)
- else:
- raise TypeError, "dictionary key must be string"
+ if type(k) is StringType:
+ _check_str(k, self.encoding)
+ elif unicode and type(k) is UnicodeType:
+ _check_unicode(k)
+ k = k.encode(self.encoding)
+ else:
+ raise TypeError, "dictionary key must be string"
write("%s\n" % escape(k))
dump(v, write)
write("\n")