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