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):

`