github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/gubernator/third_party/defusedxml/xmlrpc.py (about) 1 # defusedxml 2 # 3 # Copyright (c) 2013 by Christian Heimes <christian@python.org> 4 # Licensed to PSF under a Contributor Agreement. 5 # See http://www.python.org/psf/license for licensing details. 6 """Defused xmlrpclib 7 8 Also defuses gzip bomb 9 """ 10 from __future__ import print_function, absolute_import 11 12 import io 13 14 from .common import (DTDForbidden, EntitiesForbidden, 15 ExternalReferenceForbidden, PY3, PY31, PY26) 16 17 if PY3: 18 __origin__ = "xmlrpc.client" 19 from xmlrpc.client import ExpatParser 20 from xmlrpc import client as xmlrpc_client 21 from xmlrpc import server as xmlrpc_server 22 if not PY31: 23 from xmlrpc.client import gzip_decode as _orig_gzip_decode 24 from xmlrpc.client import GzipDecodedResponse as _OrigGzipDecodedResponse 25 else: 26 __origin__ = "xmlrpclib" 27 from xmlrpclib import ExpatParser 28 import xmlrpclib as xmlrpc_client 29 xmlrpc_server = None 30 if not PY26: 31 from xmlrpclib import gzip_decode as _orig_gzip_decode 32 from xmlrpclib import GzipDecodedResponse as _OrigGzipDecodedResponse 33 34 try: 35 import gzip 36 except ImportError: 37 gzip = None 38 39 40 # Limit maximum request size to prevent resource exhaustion DoS 41 # Also used to limit maximum amount of gzip decoded data in order to prevent 42 # decompression bombs 43 # A value of -1 or smaller disables the limit 44 MAX_DATA = 30 * 1024 * 1024 # 30 MB 45 46 def defused_gzip_decode(data, limit=None): 47 """gzip encoded data -> unencoded data 48 49 Decode data using the gzip content encoding as described in RFC 1952 50 """ 51 if not gzip: 52 raise NotImplementedError 53 if limit is None: 54 limit = MAX_DATA 55 f = io.BytesIO(data) 56 gzf = gzip.GzipFile(mode="rb", fileobj=f) 57 try: 58 if limit < 0: # no limit 59 decoded = gzf.read() 60 else: 61 decoded = gzf.read(limit + 1) 62 except IOError: 63 raise ValueError("invalid data") 64 f.close() 65 gzf.close() 66 if limit >= 0 and len(decoded) > limit: 67 raise ValueError("max gzipped payload length exceeded") 68 return decoded 69 70 71 class DefusedGzipDecodedResponse(gzip.GzipFile if gzip else object): 72 """a file-like object to decode a response encoded with the gzip 73 method, as described in RFC 1952. 74 """ 75 def __init__(self, response, limit=None): 76 #response doesn't support tell() and read(), required by 77 #GzipFile 78 if not gzip: 79 raise NotImplementedError 80 self.limit = limit = limit if limit is not None else MAX_DATA 81 if limit < 0: # no limit 82 data = response.read() 83 self.readlength = None 84 else: 85 data = response.read(limit + 1) 86 self.readlength = 0 87 if limit >= 0 and len(data) > limit: 88 raise ValueError("max payload length exceeded") 89 self.stringio = io.BytesIO(data) 90 gzip.GzipFile.__init__(self, mode="rb", fileobj=self.stringio) 91 92 def read(self, n): 93 if self.limit >= 0: 94 left = self.limit - self.readlength 95 n = min(n, left + 1) 96 data = gzip.GzipFile.read(self, n) 97 self.readlength += len(data) 98 if self.readlength > self.limit: 99 raise ValueError("max payload length exceeded") 100 return data 101 else: 102 return gzip.GzipFile.read(self, n) 103 104 def close(self): 105 gzip.GzipFile.close(self) 106 self.stringio.close() 107 108 109 class DefusedExpatParser(ExpatParser): 110 def __init__(self, target, forbid_dtd=False, forbid_entities=True, 111 forbid_external=True): 112 ExpatParser.__init__(self, target) 113 self.forbid_dtd = forbid_dtd 114 self.forbid_entities = forbid_entities 115 self.forbid_external = forbid_external 116 parser = self._parser 117 if self.forbid_dtd: 118 parser.StartDoctypeDeclHandler = self.defused_start_doctype_decl 119 if self.forbid_entities: 120 parser.EntityDeclHandler = self.defused_entity_decl 121 parser.UnparsedEntityDeclHandler = self.defused_unparsed_entity_decl 122 if self.forbid_external: 123 parser.ExternalEntityRefHandler = self.defused_external_entity_ref_handler 124 125 def defused_start_doctype_decl(self, name, sysid, pubid, 126 has_internal_subset): 127 raise DTDForbidden(name, sysid, pubid) 128 129 def defused_entity_decl(self, name, is_parameter_entity, value, base, 130 sysid, pubid, notation_name): 131 raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name) 132 133 def defused_unparsed_entity_decl(self, name, base, sysid, pubid, 134 notation_name): 135 # expat 1.2 136 raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name) 137 138 def defused_external_entity_ref_handler(self, context, base, sysid, 139 pubid): 140 raise ExternalReferenceForbidden(context, base, sysid, pubid) 141 142 143 def monkey_patch(): 144 xmlrpc_client.FastParser = DefusedExpatParser 145 if PY26 or PY31: 146 # Python 2.6 and 3.1 have no gzip support in xmlrpc 147 return 148 xmlrpc_client.GzipDecodedResponse = DefusedGzipDecodedResponse 149 xmlrpc_client.gzip_decode = defused_gzip_decode 150 if xmlrpc_server: 151 xmlrpc_server.gzip_decode = defused_gzip_decode 152 153 def unmonkey_patch(): 154 xmlrpc_client.FastParser = None 155 if PY26 or PY31: 156 return 157 xmlrpc_client.GzipDecodedResponse = _OrigGzipDecodedResponse 158 xmlrpc_client.gzip_decode = _orig_gzip_decode 159 if xmlrpc_server: 160 xmlrpc_server.gzip_decode = _orig_gzip_decode