bpo-17239: Disable external entities in SAX parser (GH-9217) · python/cpython@17b1d5d (original) (raw)
`@@ -13,13 +13,14 @@
`
13
13
`from xml.sax.saxutils import XMLGenerator, escape, unescape, quoteattr, \
`
14
14
`XMLFilterBase, prepare_input_source
`
15
15
`from xml.sax.expatreader import create_parser
`
16
``
`-
from xml.sax.handler import feature_namespaces
`
``
16
`+
from xml.sax.handler import feature_namespaces, feature_external_ges
`
17
17
`from xml.sax.xmlreader import InputSource, AttributesImpl, AttributesNSImpl
`
18
18
`from io import BytesIO, StringIO
`
19
19
`import codecs
`
20
20
`import gc
`
21
21
`import os.path
`
22
22
`import shutil
`
``
23
`+
from urllib.error import URLError
`
23
24
`from test import support
`
24
25
`from test.support import findfile, run_unittest, TESTFN
`
25
26
``
`@@ -911,6 +912,18 @@ def notationDecl(self, name, publicId, systemId):
`
911
912
`def unparsedEntityDecl(self, name, publicId, systemId, ndata):
`
912
913
`self._entities.append((name, publicId, systemId, ndata))
`
913
914
``
``
915
+
``
916
`+
class TestEntityRecorder:
`
``
917
`+
def init(self):
`
``
918
`+
self.entities = []
`
``
919
+
``
920
`+
def resolveEntity(self, publicId, systemId):
`
``
921
`+
self.entities.append((publicId, systemId))
`
``
922
`+
source = InputSource()
`
``
923
`+
source.setPublicId(publicId)
`
``
924
`+
source.setSystemId(systemId)
`
``
925
`+
return source
`
``
926
+
914
927
`def test_expat_dtdhandler(self):
`
915
928
`parser = create_parser()
`
916
929
`handler = self.TestDTDHandler()
`
`@@ -927,6 +940,32 @@ def test_expat_dtdhandler(self):
`
927
940
` [("GIF", "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN", None)])
`
928
941
`self.assertEqual(handler._entities, [("img", None, "expat.gif", "GIF")])
`
929
942
``
``
943
`+
def test_expat_external_dtd_enabled(self):
`
``
944
`+
parser = create_parser()
`
``
945
`+
parser.setFeature(feature_external_ges, True)
`
``
946
`+
resolver = self.TestEntityRecorder()
`
``
947
`+
parser.setEntityResolver(resolver)
`
``
948
+
``
949
`+
with self.assertRaises(URLError):
`
``
950
`+
parser.feed(
`
``
951
`+
'\n'
`
``
952
`+
)
`
``
953
`+
self.assertEqual(
`
``
954
`+
resolver.entities, [(None, 'unsupported://non-existing')]
`
``
955
`+
)
`
``
956
+
``
957
`+
def test_expat_external_dtd_default(self):
`
``
958
`+
parser = create_parser()
`
``
959
`+
resolver = self.TestEntityRecorder()
`
``
960
`+
parser.setEntityResolver(resolver)
`
``
961
+
``
962
`+
parser.feed(
`
``
963
`+
'\n'
`
``
964
`+
)
`
``
965
`+
parser.feed('')
`
``
966
`+
parser.close()
`
``
967
`+
self.assertEqual(resolver.entities, [])
`
``
968
+
930
969
`# ===== EntityResolver support
`
931
970
``
932
971
`class TestEntityResolver:
`
`@@ -936,8 +975,9 @@ def resolveEntity(self, publicId, systemId):
`
936
975
`inpsrc.setByteStream(BytesIO(b""))
`
937
976
`return inpsrc
`
938
977
``
939
``
`-
def test_expat_entityresolver(self):
`
``
978
`+
def test_expat_entityresolver_enabled(self):
`
940
979
`parser = create_parser()
`
``
980
`+
parser.setFeature(feature_external_ges, True)
`
941
981
`parser.setEntityResolver(self.TestEntityResolver())
`
942
982
`result = BytesIO()
`
943
983
`parser.setContentHandler(XMLGenerator(result))
`
`@@ -951,6 +991,22 @@ def test_expat_entityresolver(self):
`
951
991
`self.assertEqual(result.getvalue(), start +
`
952
992
`b"")
`
953
993
``
``
994
`+
def test_expat_entityresolver_default(self):
`
``
995
`+
parser = create_parser()
`
``
996
`+
self.assertEqual(parser.getFeature(feature_external_ges), False)
`
``
997
`+
parser.setEntityResolver(self.TestEntityResolver())
`
``
998
`+
result = BytesIO()
`
``
999
`+
parser.setContentHandler(XMLGenerator(result))
`
``
1000
+
``
1001
`+
parser.feed('<!DOCTYPE doc [\n')
`
``
1002
`+
parser.feed(' \n')
`
``
1003
`+
parser.feed(']>\n')
`
``
1004
`+
parser.feed('&test;')
`
``
1005
`+
parser.close()
`
``
1006
+
``
1007
`+
self.assertEqual(result.getvalue(), start +
`
``
1008
`+
b"")
`
``
1009
+
954
1010
`# ===== Attributes support
`
955
1011
``
956
1012
`class AttrGatherer(ContentHandler):
`