diff --git a/CVE-2023-33733.patch b/CVE-2023-33733.patch deleted file mode 100644 index 03278c0..0000000 --- a/CVE-2023-33733.patch +++ /dev/null @@ -1,346 +0,0 @@ -From 28ae88f7d2994c54064ccb140d6a44652135cb7a Mon Sep 17 00:00:00 2001 -From: starlet-dx <15929766099@163.com> -Date: Wed, 26 Jul 2023 10:18:30 +0800 -Subject: [PATCH 1/1] rl_safe_eval & rl_config.toColorCanUse changes - -Refer: -https://hg.reportlab.com/hg-public/reportlab/raw-rev/1c39d2db15bb ---- - src/reportlab/lib/colors.py | 75 +++++++++++++++++++++++++------ - src/reportlab/lib/rl_safe_eval.py | 71 ++++++++++++++++++++++++++++- - src/reportlab/lib/utils.py | 2 +- - src/reportlab/rl_settings.py | 4 +- - tests/test_lib_rl_safe_eval.py | 49 ++++++++++++++++++-- - 5 files changed, 180 insertions(+), 21 deletions(-) - -diff --git a/src/reportlab/lib/colors.py b/src/reportlab/lib/colors.py -index 84e8679..09d078f 100644 ---- a/src/reportlab/lib/colors.py -+++ b/src/reportlab/lib/colors.py -@@ -41,7 +41,8 @@ ValueError: css color 'pcmyka(100,0,0,0)' has wrong number of components - ''' - import math, re, functools - from reportlab.lib.rl_accel import fp_str --from reportlab.lib.utils import asNative, isStr, rl_safe_eval -+from reportlab.lib.utils import asNative, isStr, rl_safe_eval, rl_extended_literal_eval -+from reportlab import rl_config - from ast import literal_eval - - class Color: -@@ -835,6 +836,17 @@ class cssParse: - cssParse=cssParse() - - class toColor: -+ """Accepot an expression returnng a Color subclass. -+ -+ This used to accept arbitrary Python expressions, which resulted in increasngly devilish CVEs and -+ security holes from tie to time. In April 2023 we are creating explicit, "dumb" parsing code to -+ replace this. Acceptable patterns are -+ -+ a Color instance passed in by the Python programmer -+ a named list of colours ('pink' etc') -+ list of 3 or 4 numbers -+ all CSS colour expression -+ """ - _G = {} #globals we like (eventually) - - def __init__(self): -@@ -860,21 +872,58 @@ class toColor: - C = getAllNamedColors() - s = arg.lower() - if s in C: return C[s] -- G = C.copy() -- G.update(self.extraColorsNS) -- if not self._G: -- C = globals() -- self._G = {s:C[s] for s in '''Blacker CMYKColor CMYKColorSep Color ColorType HexColor PCMYKColor PCMYKColorSep Whiter -- _chooseEnforceColorSpace _enforceCMYK _enforceError _enforceRGB _enforceSEP _enforceSEP_BLACK -- _enforceSEP_CMYK _namedColors _re_css asNative cmyk2rgb cmykDistance color2bw colorDistance -- cssParse describe fade fp_str getAllNamedColors hsl2rgb hue2rgb isStr linearlyInterpolatedColor -- literal_eval obj_R_G_B opaqueColor rgb2cmyk setColors toColor toColorOrNone'''.split()} -- G.update(self._G) -+ -+ -+ # allow expressions like 'Blacker(red, 0.5)' -+ # >>> re.compile(r"(Blacker|Whiter)\((\w+)\,\s?([0-9.]+)\)").match(msg).groups() -+ # ('Blacker', 'red', '0.5') -+ # >>> -+ pat = re.compile(r"(Blacker|Whiter)\((\w+)\,\s?([0-9.]+)\)") -+ m = pat.match(arg) -+ if m: -+ funcname, rootcolor, num = m.groups() -+ if funcname == 'Blacker': -+ return Blacker(rootcolor, float(num)) -+ else: -+ return Whiter(rootcolor, float(num)) -+ - try: -- return toColor(rl_safe_eval(arg,g=G,l={})) -- except: -+ import ast -+ expr = ast.literal_eval(arg) #safe probably only a tuple or list of values -+ return toColor(expr) -+ except (SyntaxError, ValueError): - pass - -+ if rl_config.toColorCanUse=='rl_safe_eval': -+ #the most dangerous option -+ G = C.copy() -+ G.update(self.extraColorsNS) -+ if not self._G: -+ C = globals() -+ self._G = {s:C[s] for s in '''Blacker CMYKColor CMYKColorSep Color ColorType HexColor PCMYKColor PCMYKColorSep Whiter -+ _chooseEnforceColorSpace _enforceCMYK _enforceError _enforceRGB _enforceSEP _enforceSEP_BLACK -+ _enforceSEP_CMYK _namedColors _re_css asNative cmyk2rgb cmykDistance color2bw colorDistance -+ cssParse describe fade fp_str getAllNamedColors hsl2rgb hue2rgb isStr linearlyInterpolatedColor -+ literal_eval obj_R_G_B opaqueColor rgb2cmyk setColors toColor toColorOrNone'''.split()} -+ G.update(self._G) -+ try: -+ return toColor(rl_safe_eval(arg,g=G,l={})) -+ except: -+ pass -+ elif rl_config.toColorCanUse=='rl_extended_literal_eval': -+ C = globals() -+ S = getAllNamedColors().copy() -+ C = {k:C[k] for k in '''Blacker CMYKColor CMYKColorSep Color ColorType HexColor PCMYKColor PCMYKColorSep Whiter -+ _chooseEnforceColorSpace _enforceCMYK _enforceError _enforceRGB _enforceSEP _enforceSEP_BLACK -+ _enforceSEP_CMYK _namedColors _re_css asNative cmyk2rgb cmykDistance color2bw colorDistance -+ cssParse describe fade fp_str getAllNamedColors hsl2rgb hue2rgb linearlyInterpolatedColor -+ obj_R_G_B opaqueColor rgb2cmyk setColors toColor toColorOrNone'''.split() -+ if callable(C.get(k,None))} -+ try: -+ return rl_extended_literal_eval(arg,C,S) -+ except (ValueError, SyntaxError): -+ pass -+ - try: - return HexColor(arg) - except: -diff --git a/src/reportlab/lib/rl_safe_eval.py b/src/reportlab/lib/rl_safe_eval.py -index 49828c9..50834f6 100644 ---- a/src/reportlab/lib/rl_safe_eval.py -+++ b/src/reportlab/lib/rl_safe_eval.py -@@ -3,7 +3,7 @@ - #https://github.com/zopefoundation/RestrictedPython - #https://github.com/danthedeckie/simpleeval - #hopefully we are standing on giants' shoulders --import sys, os, ast, re, weakref, time, copy, math -+import sys, os, ast, re, weakref, time, copy, math, types - eval_debug = int(os.environ.get('EVAL_DEBUG','0')) - strTypes = (bytes,str) - isPy39 = sys.version_info[:2]>=(3,9) -@@ -53,7 +53,9 @@ __rl_unsafe__ = frozenset('''builtins breakpoint __annotations__ co_argcount co_ - func_doc func_globals func_name gi_code gi_frame gi_running gi_yieldfrom - __globals__ im_class im_func im_self __iter__ __kwdefaults__ __module__ - __name__ next __qualname__ __self__ tb_frame tb_lasti tb_lineno tb_next -- globals vars locals'''.split() -+ globals vars locals -+ type eval exec aiter anext compile open -+ dir print classmethod staticmethod __import__ super property'''.split() - ) - __rl_unsafe_re__ = re.compile(r'\b(?:%s)' % '|'.join(__rl_unsafe__),re.M) - -@@ -1204,5 +1206,70 @@ class __rl_safe_eval__: - class __rl_safe_exec__(__rl_safe_eval__): - mode = 'exec' - -+def rl_extended_literal_eval(expr, safe_callables=None, safe_names=None): -+ if safe_callables is None: -+ safe_callables = {} -+ if safe_names is None: -+ safe_names = {} -+ safe_names = safe_names.copy() -+ safe_names.update({'None': None, 'True': True, 'False': False}) -+ #make these readonly with MappingProxyType -+ safe_names = types.MappingProxyType(safe_names) -+ safe_callables = types.MappingProxyType(safe_callables) -+ if isinstance(expr, str): -+ expr = ast.parse(expr, mode='eval') -+ if isinstance(expr, ast.Expression): -+ expr = expr.body -+ try: -+ # Python 3.4 and up -+ ast.NameConstant -+ safe_test = lambda n: isinstance(n, ast.NameConstant) or isinstance(n,ast.Name) and n.id in safe_names -+ safe_extract = lambda n: n.value if isinstance(n,ast.NameConstant) else safe_names[n.id] -+ except AttributeError: -+ # Everything before -+ safe_test = lambda n: isinstance(n, ast.Name) and n.id in safe_names -+ safe_extract = lambda n: safe_names[n.id] -+ def _convert(node): -+ if isinstance(node, (ast.Str, ast.Bytes)): -+ return node.s -+ elif isinstance(node, ast.Num): -+ return node.n -+ elif isinstance(node, ast.Tuple): -+ return tuple(map(_convert, node.elts)) -+ elif isinstance(node, ast.List): -+ return list(map(_convert, node.elts)) -+ elif isinstance(node, ast.Dict): -+ return dict((_convert(k), _convert(v)) for k, v -+ in zip(node.keys, node.values)) -+ elif safe_test(node): -+ return safe_extract(node) -+ elif isinstance(node, ast.UnaryOp) and \ -+ isinstance(node.op, (ast.UAdd, ast.USub)) and \ -+ isinstance(node.operand, (ast.Num, ast.UnaryOp, ast.BinOp)): -+ operand = _convert(node.operand) -+ if isinstance(node.op, ast.UAdd): -+ return + operand -+ else: -+ return - operand -+ elif isinstance(node, ast.BinOp) and \ -+ isinstance(node.op, (ast.Add, ast.Sub)) and \ -+ isinstance(node.right, (ast.Num, ast.UnaryOp, ast.BinOp)) and \ -+ isinstance(node.right.n, complex) and \ -+ isinstance(node.left, (ast.Num, ast.UnaryOp, astBinOp)): -+ left = _convert(node.left) -+ right = _convert(node.right) -+ if isinstance(node.op, ast.Add): -+ return left + right -+ else: -+ return left - right -+ elif isinstance(node, ast.Call) and \ -+ isinstance(node.func, ast.Name) and \ -+ node.func.id in safe_callables: -+ return safe_callables[node.func.id]( -+ *[_convert(n) for n in node.args], -+ **{kw.arg: _convert(kw.value) for kw in node.keywords}) -+ raise ValueError('Bad expression') -+ return _convert(expr) -+ - rl_safe_exec = __rl_safe_exec__() - rl_safe_eval = __rl_safe_eval__() -diff --git a/src/reportlab/lib/utils.py b/src/reportlab/lib/utils.py -index 5a6b5d7..a53a05c 100644 ---- a/src/reportlab/lib/utils.py -+++ b/src/reportlab/lib/utils.py -@@ -11,7 +11,7 @@ from io import BytesIO - from hashlib import md5 - - from reportlab.lib.rltempfile import get_rl_tempfile, get_rl_tempdir --from . rl_safe_eval import rl_safe_exec, rl_safe_eval, safer_globals -+from . rl_safe_eval import rl_safe_exec, rl_safe_eval, safer_globals, rl_extended_literal_eval - from PIL import Image - - class __UNSET__: -diff --git a/src/reportlab/rl_settings.py b/src/reportlab/rl_settings.py -index 2c1c021..4a69cc0 100644 ---- a/src/reportlab/rl_settings.py -+++ b/src/reportlab/rl_settings.py -@@ -69,7 +69,8 @@ trustedHosts - trustedSchemes - renderPMBackend - xmlParser --textPaths'''.split()) -+textPaths -+toColorCanUse'''.split()) - - allowTableBoundsErrors = 1 # set to 0 to die on too large elements in tables in debug (recommend 1 for production use) - shapeChecking = 1 -@@ -163,6 +164,7 @@ xmlParser='lxml' #or 'pyrxp' for preferred xm - textPaths='backend' #freetype or _renderPM or backend - #determines what code is used to create Paths from str - #see reportlab/graphics/utils.py for full horror -+toColorCanUse='rl_extended_literal_eval' #change to None or 'rl_safe_eval' depending on trust - - # places to look for T1Font information - T1SearchPath = ( -diff --git a/tests/test_lib_rl_safe_eval.py b/tests/test_lib_rl_safe_eval.py -index 84bd86f..fd556eb 100644 ---- a/tests/test_lib_rl_safe_eval.py -+++ b/tests/test_lib_rl_safe_eval.py -@@ -1,6 +1,6 @@ - #Copyright ReportLab Europe Ltd. 2000-2017 - #see license.txt for license details --"""Tests for reportlab.lib.rl_eval -+"""Tests for reportlab.lib.rl_safe_eval - """ - __version__='3.5.33' - from reportlab.lib.testutils import setOutDir,makeSuiteForClasses, printLocation -@@ -10,7 +10,7 @@ import reportlab - from reportlab import rl_config - import unittest - from reportlab.lib import colors --from reportlab.lib.utils import rl_safe_eval, rl_safe_exec, annotateException -+from reportlab.lib.utils import rl_safe_eval, rl_safe_exec, annotateException, rl_extended_literal_eval - from reportlab.lib.rl_safe_eval import BadCode - - testObj = [1,('a','b',2),{'A':1,'B':2.0},"32"] -@@ -52,7 +52,6 @@ class SafeEvalTestSequenceMeta(type): - 'dict(a=1).get("a",2)', - 'dict(a=1).pop("a",2)', - '{"_":1+_ for _ in (1,2)}.pop(1,None)', -- '(type(1),type(str),type(testObj),type(TestClass))', - '1 if True else "a"', - '1 if False else "a"', - 'testFunc(bad=False)', -@@ -74,6 +73,8 @@ class SafeEvalTestSequenceMeta(type): - ( - 'fail', - ( -+ 'vars()', -+ '(type(1),type(str),type(testObj),type(TestClass))', - 'open("/tmp/myfile")', - 'SafeEvalTestCase.__module__', - ("testInst.__class__.__bases__[0].__subclasses__()",dict(g=dict(testInst=testInst))), -@@ -97,6 +98,8 @@ class SafeEvalTestSequenceMeta(type): - 'testFunc(bad=True)', - 'getattr(testInst,"__class__",14)', - '"{1}{2}".format(1,2)', -+ 'builtins', -+ '[ [ [ [ ftype(ctype(0, 0, 0, 0, 3, 67, b"t\\x00d\\x01\\x83\\x01\\xa0\\x01d\\x02\\xa1\\x01\\x01\\x00d\\x00S\\x00", (None, "os", "touch /tmp/exploited"), ("__import__", "system"), (), "", "", 1, b"\\x12\\x01"), {})() for ftype in [type(lambda: None)] ] for ctype in [type(getattr(lambda: {None}, Word("__code__")))] ] for Word in [orgTypeFun("Word", (str,), { "mutated": 1, "startswith": lambda self, x: False, "__eq__": lambda self,x: self.mutate() and self.mutated < 0 and str(self) == x, "mutate": lambda self: {setattr(self, "mutated", self.mutated - 1)}, "__hash__": lambda self: hash(str(self)) })] ] for orgTypeFun in [type(type(1))]] and "red"', - ) - ), - ): -@@ -155,8 +158,46 @@ class SafeEvalTestBasics(unittest.TestCase): - def test_002(self): - self.assertTrue(rl_safe_eval("GA=='ga'")) - -+class ExtendedLiteralEval(unittest.TestCase): -+ def test_001(self): -+ S = colors.getAllNamedColors().copy() -+ C = {s:getattr(colors,s) for s in '''Blacker CMYKColor CMYKColorSep Color ColorType HexColor PCMYKColor PCMYKColorSep Whiter -+ _chooseEnforceColorSpace _enforceCMYK _enforceError _enforceRGB _enforceSEP _enforceSEP_BLACK -+ _enforceSEP_CMYK _namedColors _re_css asNative cmyk2rgb cmykDistance color2bw colorDistance -+ cssParse describe fade fp_str getAllNamedColors hsl2rgb hue2rgb linearlyInterpolatedColor -+ obj_R_G_B opaqueColor rgb2cmyk setColors toColor toColorOrNone'''.split() -+ if callable(getattr(colors,s,None))} -+ def showVal(s): -+ try: -+ r = rl_extended_literal_eval(s,C,S) -+ except: -+ r = str(sys.exc_info()[1]) -+ return r -+ -+ for expr, expected in ( -+ ('1.0', 1.0), -+ ('1', 1), -+ ('red', colors.red), -+ ('True', True), -+ ('False', False), -+ ('None', None), -+ ('Blacker(red,0.5)', colors.Color(.5,0,0,1)), -+ ('PCMYKColor(21,10,30,5,spotName="ABCD")', colors.PCMYKColor(21,10,30,5,spotName='ABCD',alpha=100)), -+ ('HexColor("#ffffff")', colors.Color(1,1,1,1)), -+ ('linearlyInterpolatedColor(red, blue, 0, 1, 0.5)', colors.Color(.5,0,.5,1)), -+ ('red.rgb()', 'Bad expression'), -+ ('__import__("sys")', 'Bad expression'), -+ ('globals()', 'Bad expression'), -+ ('locals()', 'Bad expression'), -+ ('vars()', 'Bad expression'), -+ ('builtins', 'Bad expression'), -+ ('__file__', 'Bad expression'), -+ ('__name__', 'Bad expression'), -+ ): -+ self.assertEqual(showVal(expr),expected,f"rl_extended_literal_eval({expr!r}) is not equal to expected {expected}") -+ - def makeSuite(): -- return makeSuiteForClasses(SafeEvalTestCase,SafeEvalTestBasics) -+ return makeSuiteForClasses(SafeEvalTestCase,SafeEvalTestBasics,ExtendedLiteralEval) - - if __name__ == "__main__": #noruntests - unittest.TextTestRunner().run(makeSuite()) --- -2.30.0 - diff --git a/python-reportlab.spec b/python-reportlab.spec index f3dd42c..8f702c5 100644 --- a/python-reportlab.spec +++ b/python-reportlab.spec @@ -3,14 +3,14 @@ %bcond_without tests Name: python-reportlab -Version: 3.6.12 -Release: 3 +Version: 4.0.8 +Release: 1 Summary: ReportLab library to create PDF documents and graphic License: BSD URL: https://www.reportlab.com/ -Source0: https://files.pythonhosted.org/packages/b8/ac/10d68a650b321bd8c4d8cbefd9994e7727d57b381c9bdb0a013273011e62/reportlab-3.6.12.tar.gz -Patch0: CVE-2023-33733.patch -BuildRequires: libart_lgpl-devel freetype-devel +Source0: https://files.pythonhosted.org/packages/source/r/reportlab/reportlab-%{version}.tar.gz +BuildRequires: freetype-devel +BuildArch: noarch %description The ReportLab Toolkit. An Open Source Python library for generating PDFs and graphics. @@ -47,12 +47,12 @@ rm -rf src/reportlab.egg-info rm -rf src/rl_addons/renderPM/libart_lgpl %build -CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS} -Isrc/rl_addons/renderPM -I%{_includedir}/libart-2.0}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\ - %{__python3} setup.py --use-system-libart --no-download-t1-files build --executable="%{__python3} -s" +CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS} -Isrc/rl_addons/renderPM}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\ + %{__python3} setup.py --no-download-t1-files build --executable="%{__python3} -s" %install -CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS} -Isrc/rl_addons/renderPM -I%{_includedir}/libart-2.0}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\ - %{__python3} setup.py --use-system-libart --no-download-t1-files install -O1 --skip-build --root ${RPM_BUILD_ROOT} +CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS} -Isrc/rl_addons/renderPM}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\ + %{__python3} setup.py --no-download-t1-files install -O1 --skip-build --root ${RPM_BUILD_ROOT} %if %{with tests} %check @@ -67,13 +67,17 @@ cp -a build/lib.%{python3_platform}-cpython-%{python3_version_nodots}/reportlab %files -n python3-reportlab %doc README.txt CHANGES.md -%license LICENSE.txt -%{python3_sitearch}/{reportlab/,reportlab-%{version}-py%{python3_version}.egg-info} +%license LICENSE +%{python3_sitelib}/reportlab/ +%{python3_sitelib}/reportlab-%{version}-py%{python3_version}.egg-info %files help %doc demos/ tools/ %changelog +* Tue Jan 16 2024 xu_ping <707078654@qq.com> - 4.0.8-1 +- Upgrade version to 4.0.8 + * Wed Jul 26 2023 yaoxin - 3.6.12-3 - Fix CVE-2023-33733 diff --git a/reportlab-3.6.12.tar.gz b/reportlab-3.6.12.tar.gz deleted file mode 100644 index f5e4148..0000000 Binary files a/reportlab-3.6.12.tar.gz and /dev/null differ diff --git a/reportlab-4.0.8.tar.gz b/reportlab-4.0.8.tar.gz new file mode 100644 index 0000000..7c6dff6 Binary files /dev/null and b/reportlab-4.0.8.tar.gz differ