Commits

Olemis Lang  committed 14258ce

Trac RPC [ refs #11050 ] : Replace invalid XML characters with \uFFFD

  • Participants
  • Parent commits e122336
  • Branches t11050

Comments (0)

Files changed (2)

 # Placed by Bitbucket
+t11050/t11050_r13710_xml_invalid_chars.diff

File t11050/t11050_r13710_xml_invalid_chars.diff

+# HG changeset patch
+# Parent a61fdef743074d7451678ee58148378b357725f7
+Trac RPC [ fixes #11050 ] : Strip invalid control characters from XML (RPC) output
+
+diff -r a61fdef74307 trunk/tracrpc/tests/xml_rpc.py
+--- a/trunk/tracrpc/tests/xml_rpc.py	Thu Mar 06 13:44:39 2014 -0500
++++ b/trunk/tracrpc/tests/xml_rpc.py	Thu Mar 06 16:48:02 2014 -0500
+@@ -5,6 +5,7 @@
+ (c) 2009      ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
+ """
+ 
++import os
+ import unittest
+ 
+ import xmlrpclib
+@@ -108,6 +109,44 @@
+         self.assertEquals('Desc & ription', ticket[3]['description'])
+         self.admin.ticket.delete(tid1)
+ 
++    def test_xml_encoding_invalid_characters(self):
++        # Enable ticket manipulator
++        plugin = os.path.join(rpc_testenv.tracdir, 'plugins', 'InvalidXmlCharHandler.py')
++        open(plugin, 'w').write(
++        "from trac.core import *\n"
++        "from tracrpc.api import IXMLRPCHandler\n"
++        "class UniChr(Component):\n"
++        "    implements(IXMLRPCHandler)\n"
++        "    def xmlrpc_namespace(self):\n"
++        "        return 'test_unichr'\n"
++        "    def xmlrpc_methods(self):\n"
++        "        yield ('XML_RPC', ((str, int),), self.unichr)\n"
++        "    def unichr(self, req, code):\n"
++        "        return unichr(code)\n")
++        rpc_testenv.restart()
++
++        _illegal_unichrs = [ (0x00, 0x08), (0x0B, 0x1F), 
++                             (0x7F, 0x84), (0x86, 0x9F),
++                             (0xD800, 0xDFFF), (0xFDD0, 0xFDDF), 
++                             (0xFFFE, 0xFFFF),
++                             (0x1FFFE, 0x1FFFF), (0x2FFFE, 0x2FFFF), 
++                             (0x3FFFE, 0x3FFFF), (0x4FFFE, 0x4FFFF),
++                             (0x5FFFE, 0x5FFFF), (0x6FFFE, 0x6FFFF),
++                             (0x7FFFE, 0x7FFFF), (0x8FFFE, 0x8FFFF),
++                             (0x9FFFE, 0x9FFFF), (0xAFFFE, 0xAFFFF),
++                             (0xBFFFE, 0xBFFFF), (0xCFFFE, 0xCFFFF),
++                             (0xDFFFE, 0xDFFFF), (0xEFFFE, 0xEFFFF),
++                             (0xFFFFE, 0xFFFFF), (0x10FFFE, 0x10FFFF) ]
++
++        for low, high in _illegal_unichrs:
++            for x in (low, (low + high) / 2, high):
++                self.assertEquals(u'\uFFFD', self.user.test_unichr.unichr(x),
++                                  "Failed unichr with %d" % (x,))
++
++        # Remove plugin and restart
++        os.unlink(plugin)
++        rpc_testenv.restart()
++
+ def test_suite():
+     return unittest.makeSuite(RpcXmlTestCase)
+ 
+diff -r a61fdef74307 trunk/tracrpc/util.py
+--- a/trunk/tracrpc/util.py	Thu Mar 06 13:44:39 2014 -0500
++++ b/trunk/tracrpc/util.py	Thu Mar 06 16:48:02 2014 -0500
+@@ -64,3 +64,6 @@
+     from trac.util.datefmt import to_timestamp
+     to_utimestamp = to_timestamp
+     from_utimestamp = lambda x: to_datetime(x, utc)
++
++UNICODE_REPLACE_CHAR = u'\uFFFD'
++
+diff -r a61fdef74307 trunk/tracrpc/xml_rpc.py
+--- a/trunk/tracrpc/xml_rpc.py	Thu Mar 06 13:44:39 2014 -0500
++++ b/trunk/tracrpc/xml_rpc.py	Thu Mar 06 16:48:02 2014 -0500
+@@ -7,6 +7,8 @@
+ """
+ 
+ import datetime
++import re
++import sys
+ import time
+ import xmlrpclib
+ 
+@@ -25,7 +27,7 @@
+ 
+ from tracrpc.api import XMLRPCSystem, IRPCProtocol, Binary, \
+         RPCError, MethodNotFound, ProtocolException, ServiceException
+-from tracrpc.util import empty, prepare_docs
++from tracrpc.util import empty, prepare_docs, UNICODE_REPLACE_CHAR
+ 
+ __all__ = ['XmlRpcProtocol']
+ 
+@@ -145,8 +147,27 @@
+ 
+     # Internal methods
+ 
++    _illegal_unichrs = [ (0x00, 0x08), (0x0B, 0x1F), (0x7F, 0x84), (0x86, 0x9F),
++                         (0xD800, 0xDFFF), (0xFDD0, 0xFDDF), (0xFFFE, 0xFFFF),
++                         (0x1FFFE, 0x1FFFF), (0x2FFFE, 0x2FFFF), 
++                         (0x3FFFE, 0x3FFFF), (0x4FFFE, 0x4FFFF),
++                         (0x5FFFE, 0x5FFFF), (0x6FFFE, 0x6FFFF),
++                         (0x7FFFE, 0x7FFFF), (0x8FFFE, 0x8FFFF),
++                         (0x9FFFE, 0x9FFFF), (0xAFFFE, 0xAFFFF),
++                         (0xBFFFE, 0xBFFFF), (0xCFFFE, 0xCFFFF),
++                         (0xDFFFE, 0xDFFFF), (0xEFFFE, 0xEFFFF),
++                         (0xFFFFE, 0xFFFFF), (0x10FFFE, 0x10FFFF) ]
++
++    _illegal_ranges = ["%s-%s" % (unichr(low), unichr(high)) 
++                       for (low, high) in _illegal_unichrs 
++                                    if low < sys.maxunicode]
++
++    _illegal_xml_chars_RE = re.compile(u'[%s]' % u''.join(_illegal_ranges))
++
+     def _send_response(self, req, response, content_type='application/xml'):
+-        response = to_unicode(response).encode("utf-8")
++        response = to_unicode(response)
++        response = self._illegal_xml_chars_RE.sub(UNICODE_REPLACE_CHAR, response)
++        response = response.encode("utf-8")
+         req.send_response(200)
+         req.send_header('Content-Type', content_type)
+         req.send_header('Content-Length', len(response))