File CVE-2020-28463_fix_ssrf.patch of Package python-reportlab.29358

# HG changeset patch
# User robin
# Date 1603974725 0
# Node ID 7f2231703dc7ba8d0758550936d1366fb91a64d3
# Parent  e1390a6dfd33ae5f296153bc63df0256cd7e8fd9
added trustedHosts & trustedSchemes settings; version --> 3.5.55

Index: reportlab-3.4.0/docs/userguide/ch1_intro.py
===================================================================
--- reportlab-3.4.0.orig/docs/userguide/ch1_intro.py
+++ reportlab-3.4.0/docs/userguide/ch1_intro.py
@@ -312,8 +312,13 @@ or want to draw attention to an issue, p
 
 heading2("Site Configuration")
 disc("""There are a number of options which most likely need to be configured globally for a site.
-The python script module $reportlab/rl_config.py$ may be edited to change the values of several
-important sitewide properties.""")
+The python script module $reportlab/rl_config.py$ aggregates the various settings files. You may want inspect the file $reportlab/$rl_settings.py$ which
+contains defaults for the currently used variables. There are several overrides for $rl_settings"
+modules $reportlab.local_rl_settings$, $reportlab_settings$ (a script file anywhere on the python path)
+and finally the file $~/.reportlab_settings$ (note no .py). Temporary changes can be made using evironment variables which
+are the variables from $rl_settings.py" prefixed with $RL_$ eg $RL_verbose=1$.
+""")
+heading3("Useful rl_config variables")
 bullet("""verbose: set to integer values to control diagnostic output.""")
 bullet("""shapeChecking: set this to zero to turn off a lot of error checking in the graphics modules""")
 bullet("""defaultEncoding: set this to WinAnsiEncoding or MacRomanEncoding.""")
@@ -329,9 +334,16 @@ bullet("""CMapSearchPath: this is a pyth
 may be queried for information on font code maps.""")
 bullet("""showBoundary: set to non-zero to get boundary lines drawn.""")
 bullet("""ZLIB_WARNINGS: set to non-zero to get warnings if the Python compression extension is not found.""")
-bullet("""pageComression: set to non-zero to try and get compressed PDF.""")
+bullet("""pageCompression: set to non-zero to try and get compressed PDF.""")
 bullet("""allowtableBoundsErrors: set to 0 to force an error on very large Platypus table elements""")
 bullet("""emptyTableAction: Controls behaviour for empty tables, can be 'error' (default), 'indicate' or 'ignore'.""")
+bullet("""trustedHosts: if not $None$ a list of glob patterns of trusted hosts; these may be used in places like <img> tags in paragraph texts.""")
+bullet("""trustedSchemes: a list of allowed $URL$ schemes used with $trustedHosts$""")
+disc("""For the full list of variables see the file $reportlab/rl_settings.py$.""")
+heading3("Other modifications")
+disc("""More complex modifications to the reportlab toolkit environment may be made using one
+of the modules $rep[ortlab.local_rl_mods$ (.py script in reportlab folder),
+$reportlab_mods$ (.py file on the python path) or $~/.reportlab_mods$ (note no .py).""")
 
 heading2("Learning More About Python")
 
@@ -385,11 +397,10 @@ be standard in future Ubuntu releases an
 of major Python packages now run on Python 3.  """)
 
 
-
-bullet("""Python 3.x compatibility.  A single line of code should run on 2.7 and 3.3""")
-bullet(""" __init__.py restricts to 2.7 or >=3.3""")
+bullet("""Python 3.x compatibility.  A single line of code should run on 2.7 and 3.6""")
+bullet(""" __init__.py restricts to 2.7 or >=3.6""")
 bullet("""__init__.py allow the import of on optional reportlab.local_rl_mods to allow monkey patching etc.""")
-bullet("""rl_config now imports rl_settings & optionally local_rl_settings""")
+bullet("""rl_config now imports rl_settings, optionally local_rl_settings, reportlab_settings.py & finally ~/.reportlab_settings""")
 bullet("""ReportLab C extensions now live inside reportlab; _rl_accel is no longer required. All _rl_accel imports now pass through reportlab.lib.rl_accel""")
 bullet("""xmllib is gone, alongside the paraparser stuff that caused issues in favour of HTMLParser.""")
 bullet("""some obsolete C extensions (sgmlop and pyHnj) are gone""")
Index: reportlab-3.4.0/docs/userguide/ch2a_fonts.py
===================================================================
--- reportlab-3.4.0.orig/docs/userguide/ch2a_fonts.py
+++ reportlab-3.4.0/docs/userguide/ch2a_fonts.py
@@ -216,7 +216,7 @@ for Type-1 fonts.
 disc("""
 Unfortunately, there is no reliable standard yet for such
 locations (not even on the same platform) and, hence, you might
-have to edit the file $reportlab/rl_config.py$ to modify the
+have to edit one of the files $reportlab_settings.py$ or $~/.reportlab_settings$ to modify the
 value of the $T1SearchPath$ identifier to contain additional
 directories.  Our own recommendation is to use the ^reportlab/fonts^
 folder in development; and to have any needed fonts as packaged parts of
Index: reportlab-3.4.0/docs/userguide/ch5_paragraphs.py
===================================================================
--- reportlab-3.4.0.orig/docs/userguide/ch5_paragraphs.py
+++ reportlab-3.4.0/docs/userguide/ch5_paragraphs.py
@@ -308,6 +308,9 @@ This &lt;img/&gt; <img src="../images/te
 This &lt;img/&gt; <img src="../images/testimg.gif" valign="+4"/> is aligned <b>+4</b>.<br/><br/>
 This &lt;img/&gt; <img src="../images/testimg.gif" width="10"/> has width <b>10</b>.<br/><br/>
 </para>""","Inline images")
+disc("""The $src$ attribute can refer to a remote location eg $src="https://www.reportlab.com/images/logo.gif"$. By default we set $rl_config.trustedShemes$ to $['https','http', 'file', 'data', 'ftp']$ and
+$rl_config.trustedHosts=None$ the latter meaning no-restriction. You can modify these variables using one of the override files eg $reportlab_settings.py$ or $~/.reportlab_settings$. Or as comma seprated strings in the 
+environment variables $RL_trustedSchemes$ &amp; $RL_trustedHosts$. Note that the $trustedHosts$ values may contain <b>glob</b> wild cars so <i>*.reportlab.com</i> will match the obvious domains.""")
 
 heading3("Numbering Paragraphs and Lists")
 disc("""The $&lt;seq&gt;$ tag provides comprehensive support
Index: reportlab-3.4.0/src/reportlab/lib/utils.py
===================================================================
--- reportlab-3.4.0.orig/src/reportlab/lib/utils.py
+++ reportlab-3.4.0/src/reportlab/lib/utils.py
@@ -620,59 +620,73 @@ def open_for_read_by_name(name,mode='b')
         if 'b' not in mode and os.linesep!='\n': s = s.replace(os.linesep,'\n')
         return getBytesIO(s)
 
-if not isPy3:
-    import urllib2, urllib
-    urlopen=urllib2.urlopen
-    def datareader(url,opener=urllib.URLopener().open):
-        return opener(url).read()
-    del urllib, urllib2
-else:
-    from urllib.request import urlopen
-    from urllib.parse import unquote
-    import base64
-    #copied here from urllib.URLopener.open_data because
-    # 1) they want to remove it
-    # 2) the existing one is borken
-    def datareader(url, unquote=unquote, decodebytes=base64.decodebytes):
-        """Use "data" URL."""
-        # ignore POSTed data
-        #
-        # syntax of data URLs:
-        # dataurl   := "data:" [ mediatype ] [ ";base64" ] "," data
-        # mediatype := [ type "/" subtype ] *( ";" parameter )
-        # data      := *urlchar
-        # parameter := attribute "=" value
+def open_for_read(name,mode='b'):
+    #auto initialized function`
+    if not isPy3:
+        import urllib2, urllib
+        from urlparse import urlparse
+        urlopen=urllib2.urlopen
+        def datareader(url,opener=urllib.URLopener().open):
+            return opener(url).read()
+        del urllib, urllib2
+    else:
+        from urllib.request import urlopen
+        from urllib.parse import unquote, urlparse
+        #copied here from urllib.URLopener.open_data because
+        # 1) they want to remove it
+        # 2) the existing one is borken
+        def datareader(url, unquote=unquote):
+            """Use "data" URL."""
+            # ignore POSTed data
+            #
+            # syntax of data URLs:
+            # dataurl   := "data:" [ mediatype ] [ ";base64" ] "," data
+            # mediatype := [ type "/" subtype ] *( ";" parameter )
+            # data      := *urlchar
+            # parameter := attribute "=" value
+            try:
+                typ, data = url.split(',', 1)
+            except ValueError:
+                raise IOError('data error', 'bad data URL')
+            if not typ:
+                typ = 'text/plain;charset=US-ASCII'
+            semi = typ.rfind(';')
+            if semi >= 0 and '=' not in typ[semi:]:
+                encoding = typ[semi+1:]
+                typ = typ[:semi]
+            else:
+                encoding = ''
+            if encoding == 'base64':
+                # XXX is this encoding/decoding ok?
+                data = base64_decodebytes(data.encode('ascii'))
+            else:
+                data = unquote(data).encode('latin-1')
+            return data
+    from reportlab.rl_config import trustedHosts, trustedSchemes
+    if trustedHosts:
+        import re, fnmatch
+        def xre(s):
+            s = fnmatch.translate(s)
+            return s[4:-3] if s.startswith('(?s:') else s[:-7]
+        trustedHosts = re.compile(''.join(('^(?:',
+                                '|'.join(map(xre,trustedHosts)),
+                                ')\\Z')))
+    def open_for_read(name,mode='b'):
+        '''attempt to open a file or URL for reading'''
+        if hasattr(name,'read'): return name
         try:
-            typ, data = url.split(',', 1)
-        except ValueError:
-            raise IOError('data error', 'bad data URL')
-        if not typ:
-            typ = 'text/plain;charset=US-ASCII'
-        semi = typ.rfind(';')
-        if semi >= 0 and '=' not in typ[semi:]:
-            encoding = typ[semi+1:]
-            typ = typ[:semi]
-        else:
-            encoding = ''
-        if encoding == 'base64':
-            # XXX is this encoding/decoding ok?
-            data = decodebytes(data.encode('ascii'))
-        else:
-            data = unquote(data).encode('latin-1')
-        return data
-    del unquote, base64
-
-def open_for_read(name,mode='b', urlopen=urlopen, datareader=datareader):
-    '''attempt to open a file or URL for reading'''
-    if hasattr(name,'read'): return name
-    try:
-        return open_for_read_by_name(name,mode)
-    except:
-        try:
-            return getBytesIO(datareader(name) if name[:5].lower()=='data:' else urlopen(name).read())
+            return open_for_read_by_name(name,mode)
         except:
-            raise IOError('Cannot open resource "%s"' % name)
-del urlopen, datareader
+            try:
+                if trustedHosts is not None:
+                    purl = urlparse(name)
+                    if purl[0] and not ((purl[0] in ('data','file') or trustedHosts.match(purl[1])) and (purl[0] in trustedSchemes)):
+                        raise ValueError('Attempted untrusted host access')
+                return getBytesIO(datareader(name) if name[:5].lower()=='data:' else urlopen(name).read())
+            except:
+                raise IOError('Cannot open resource "%s"' % name)
+    globals()['open_for_read'] = open_for_read
+    return open_for_read(name,mode)
 
 def open_and_read(name,mode='b'):
     f = open_for_read(name,mode)
Index: reportlab-3.4.0/src/reportlab/rl_config.py
===================================================================
--- reportlab-3.4.0.orig/src/reportlab/rl_config.py
+++ reportlab-3.4.0/src/reportlab/rl_config.py
@@ -104,8 +104,12 @@ def _startUp():
             globals()[k] = list(filter(rl_isdir,globals()[k]))
         else:
             v = _SAVED[k]
-            if isinstance(v,(int,float)): conv = type(v)
-            elif k=='defaultPageSize': conv = lambda v,M=pagesizes: getattr(M,v)
+            if isinstance(v,(int,float)):
+                conv = type(v)
+            elif k=='defaultPageSize':
+                conv = lambda v,M=pagesizes: getattr(M,v)
+            elif k in ('trustedHosts','trustedSchemes'):
+                conv = lambda v: None if v is None else [y for y in [x.strip() for x in v.split(',')] if y] if isinstance(v,str) else v
             else: conv = None
             _setOpt(k,v,conv)
 
Index: reportlab-3.4.0/src/reportlab/rl_settings.py
===================================================================
--- reportlab-3.4.0.orig/src/reportlab/rl_settings.py
+++ reportlab-3.4.0/src/reportlab/rl_settings.py
@@ -53,7 +53,9 @@ decimalSymbol
 errorOnDuplicatePageLabelPage
 autoGenerateMissingTTFName
 allowTTFSubsetting
-spaceShrinkage'''.split())
+spaceShrinkage
+trustedHosts
+trustedSchemes'''.split())
 
 allowTableBoundsErrors =    1 # set to 0 to die on too large elements in tables in debug (recommend 1 for production use)
 shapeChecking =             1
@@ -116,7 +118,11 @@ allowTTFSubsetting=         []
                                                     #ReportLab takes no responsibility for the use of this setting.
 
 spaceShrinkage=0.05                                 #allowable space shrinkage to make lines fit
-
+trustedHosts=None                                   #set to a list of trusted for access hosts None means
+                                                    #all are trusted glob patterns eg *.reportlab.com are
+                                                    #allowed. In environment use a comma separated string.
+trustedSchemes=['file', 'rml', 'data', 'https',     #these url schemes are trusted
+                'http', 'ftp']
 
 # places to look for T1Font information
 T1SearchPath =  (
Index: reportlab-3.4.0/tests/runAll.py
===================================================================
--- reportlab-3.4.0.orig/tests/runAll.py
+++ reportlab-3.4.0/tests/runAll.py
@@ -11,6 +11,7 @@ import os, glob, sys, traceback, unittes
 #directory and run this directly
 if __name__=='__main__':
     P=[]
+    os.environ['RL_trustedHosts'] = '*.reportlab.com'
     try:
         from reportlab.lib.testutils import setOutDir
     except ImportError:
Index: reportlab-3.4.0/tests/test_lib_rl_safe_eval.py
===================================================================
--- reportlab-3.4.0.orig/tests/test_lib_rl_safe_eval.py
+++ reportlab-3.4.0/tests/test_lib_rl_safe_eval.py
@@ -107,7 +107,7 @@ class SafeEvalTestSequenceMeta(type):
             tfmt = 'test_ExpectedTo%s_%%02d' % kind.capitalize()
             for i, expr in enumerate(_data):
                 if expr is None:
-                    test = genTest('skip','')
+                    continue #test = genTest('skip','')
                 else:
                     expr, kwds = expr if isinstance(expr,tuple) else (expr,{})
                     test = genTest(kind, expr,**kwds)
openSUSE Build Service is sponsored by