Your message dated Sun, 13 Oct 2024 11:18:12 +0000
with message-id <zwusdaldkyvww...@inutil.org>
and subject line Re: Bug#1085009: bookworm-pu: package 
python-reportlab/3.6.12-1+deb12u1
has caused the Debian Bug report #1085009,
regarding bookworm-pu: package python-reportlab/3.6.12-1+deb12u1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact ow...@bugs.debian.org
immediately.)


-- 
1085009: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1085009
Debian Bug Tracking System
Contact ow...@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bookworm
X-Debbugs-Cc: python-report...@packages.debian.org, secur...@debian.org
Control: affects -1 + src:python-reportlab
User: release.debian....@packages.debian.org
Usertags: pu
Control: tags -1 + security

[ Reason ]
CVE-2023-33733

[ Impact ]
RCE

[ Tests ]
Yes package test run at build time

[ Risks ]
Low

[ Checklist ]
  [X] *all* changes are documented in the d/changelog
  [X] I reviewed all changes and I approve them
  [X] attach debdiff against the package in (old)stable
  [X] the issue is verified as fixed in unstable

[ Changes ]
- CVE-2023-33733 fix
- salsa CI

[ Other info ]
Did you prefer a DSA upload or a PU
diff -Nru python-reportlab-3.6.12/debian/changelog python-reportlab-3.6.12/debian/changelog
--- python-reportlab-3.6.12/debian/changelog	2022-12-31 10:05:33.000000000 +0000
+++ python-reportlab-3.6.12/debian/changelog	2024-10-12 17:14:35.000000000 +0000
@@ -1,3 +1,13 @@
+python-reportlab (3.6.12-1+deb12u1) bookworm-security; urgency=high
+
+  * Team upload
+  * Fix CVE-2023-33733
+    Reportlab was vulnerable to Remote Code Execution (RCE)
+    via crafted PDF file.
+  * Add SalsaCI
+
+ -- Bastien Roucari??s <ro...@debian.org>  Sat, 12 Oct 2024 17:14:35 +0000
+
 python-reportlab (3.6.12-1) unstable; urgency=medium
 
   * New upstream version.
diff -Nru python-reportlab-3.6.12/debian/patches/0005-CVE-2023-33733-RCE-via-crafted-PDF-file.patch python-reportlab-3.6.12/debian/patches/0005-CVE-2023-33733-RCE-via-crafted-PDF-file.patch
--- python-reportlab-3.6.12/debian/patches/0005-CVE-2023-33733-RCE-via-crafted-PDF-file.patch	1970-01-01 00:00:00.000000000 +0000
+++ python-reportlab-3.6.12/debian/patches/0005-CVE-2023-33733-RCE-via-crafted-PDF-file.patch	2024-10-12 17:14:35.000000000 +0000
@@ -0,0 +1,344 @@
+From: Robin Becker <reportlab-us...@lists2.reportlab.com>
+Date: Mon, 24 Apr 2023 13:52:40 +0100
+Subject: CVE-2023-33733 RCE via crafted PDF file
+
+origin: https://hg.reportlab.com/hg-public/reportlab/rev/1c39d2db15bb
+bug: https://github.com/c53elyas/CVE-2023-33733
+---
+ src/reportlab/lib/colors.py       | 79 +++++++++++++++++++++++++++++++--------
+ 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, 181 insertions(+), 24 deletions(-)
+
+diff --git a/src/reportlab/lib/colors.py b/src/reportlab/lib/colors.py
+index b7487db..839a674 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:
+@@ -882,6 +883,17 @@ def parseColorClassFromString(arg):
+                 return None
+ 
+ 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):
+@@ -907,22 +919,57 @@ 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:
++
++
++            # 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:
++                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()
+-                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
+-            parsedColor = parseColorClassFromString(arg)
+-            if (parsedColor): return parsedColor
++                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)
+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 0cf5150..a11289f 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"), (), "<stdin>", "", 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())
diff -Nru python-reportlab-3.6.12/debian/patches/series python-reportlab-3.6.12/debian/patches/series
--- python-reportlab-3.6.12/debian/patches/series	2021-03-13 12:29:10.000000000 +0000
+++ python-reportlab-3.6.12/debian/patches/series	2024-10-12 17:14:35.000000000 +0000
@@ -2,3 +2,4 @@
 reproducible-build.patch
 toColor.patch
 reportlab-version.diff
+0005-CVE-2023-33733-RCE-via-crafted-PDF-file.patch
diff -Nru python-reportlab-3.6.12/debian/salsa-ci.yml python-reportlab-3.6.12/debian/salsa-ci.yml
--- python-reportlab-3.6.12/debian/salsa-ci.yml	1970-01-01 00:00:00.000000000 +0000
+++ python-reportlab-3.6.12/debian/salsa-ci.yml	2024-10-12 17:14:35.000000000 +0000
@@ -0,0 +1,12 @@
+# For more information on what jobs are run see:
+# https://salsa.debian.org/salsa-ci-team/pipeline
+#
+# To enable the jobs, go to your repository (at salsa.debian.org)
+# and click over Settings > CI/CD > Expand (in General pipelines).
+# In "CI/CD configuration file" write debian/salsa-ci.yml and click
+# in "Save Changes". The CI tests will run after the next commit.
+---
+include:
+  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml
+variables:
+  RELEASE: 'bookworm'

Attachment: signature.asc
Description: This is a digitally signed message part.


--- End Message ---
--- Begin Message ---
On Sat, Oct 12, 2024 at 07:36:46PM +0000, Bastien Roucariès wrote:
> Package: release.debian.org
> Severity: normal
> Tags: bookworm
> X-Debbugs-Cc: python-report...@packages.debian.org, secur...@debian.org
> Control: affects -1 + src:python-reportlab
> User: release.debian....@packages.debian.org
> Usertags: pu
> Control: tags -1 + security
> 
> [ Reason ]
> CVE-2023-33733
> 
> [ Impact ]
> RCE
> 
> [ Tests ]
> Yes package test run at build time
> 
> [ Risks ]
> Low
> 
> [ Checklist ]
>   [X] *all* changes are documented in the d/changelog
>   [X] I reviewed all changes and I approve them
>   [X] attach debdiff against the package in (old)stable
>   [X] the issue is verified as fixed in unstable
> 
> [ Changes ]
> - CVE-2023-33733 fix
> - salsa CI
> 
> [ Other info ]
> Did you prefer a DSA upload or a PU

It's listed in data/dsa-needed.txt and the debdiff looks fine, so please build 
with -sa and upload
to security-master. Thanks!

Cheers,
        Moritz

--- End Message ---

Reply via email to