Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python311 for openSUSE:Factory checked in at 2026-06-19 16:31:07 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python311 (Old) and /work/SRC/openSUSE:Factory/.python311.new.1956 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python311" Fri Jun 19 16:31:07 2026 rev:69 rq:1357976 version:3.11.15 Changes: -------- --- /work/SRC/openSUSE:Factory/python311/python311.changes 2026-04-15 16:03:11.951616173 +0200 +++ /work/SRC/openSUSE:Factory/.python311.new.1956/python311.changes 2026-06-19 17:22:07.987636642 +0200 @@ -1,0 +2,59 @@ +Sat Jun 6 00:00:00 UTC 2026 - Matej Cepl <[email protected]> + +- Keep unversioned Python 3 development entry points in + python3-devel: python313-devel no longer provides python3-devel + and no longer owns libpython3.so, python3-config, python3.pc, + or python3-embed.pc. Do not package versioned GIL pkg-config + files in nogil-devel. Also, fix regular expressions in + rpmlintrc. + +------------------------------------------------------------------- +Fri May 1 16:05:16 UTC 2026 - Matej Cepl <[email protected]> + +- Remove macros.python3. + +------------------------------------------------------------------- +Mon Apr 27 13:48:41 UTC 2026 - Matej Cepl <[email protected]> + +- CVE-2026-6019: protect against HTML injection by + Base64-encoding cookie values embedded in JS (bsc#1262654, + gh#python/cpython#90309) + CVE-2026-6019-Morsel-js_output.patch + +------------------------------------------------------------------- +Sat Apr 25 16:42:59 UTC 2026 - Matej Cepl <[email protected]> + +- CVE-2026-1502: reject CR/LF in HTTP tunnel request headers + (bsc#1261969, gh#python/cpython#146211) + CVE-2026-1502-reject-CRLF-HTTP-tunnel.patch + +------------------------------------------------------------------- +Sat Apr 25 00:14:50 UTC 2026 - Matej Cepl <[email protected]> + +- CVE-2026-4786: fix webbrowser %action substitution bypass of + dash-prefix check (bsc#1262319, gh#python/cpython#148169) + CVE-2026-4786-webbrowser-open-action.patch + +------------------------------------------------------------------- +Fri Apr 24 17:15:39 UTC 2026 - Matej Cepl <[email protected]> + +- CVE-2026-6100: prevent dangling pointer, which can end in the + use-after-free error (bsc#1262098, gh#python/cpython#148395) + CVE-2026-6100-use-after-free-decompression.patch + +------------------------------------------------------------------- +Wed Apr 15 18:00:50 UTC 2026 - Matej Cepl <[email protected]> + +- Add CVE-2026-3446-base64-padding.patch preventing ignoring + excess Base64 data after the first padded quad (bsc#1261970, + CVE-2026-3446, gh#python/cpython#145264). + +------------------------------------------------------------------- +Wed Apr 8 16:12:36 CEST 2026 - Matej Cepl <[email protected]> + +- Rewrite structure of Python interpreter packages. + `python3*` symbols should be now provided by real python3 + packages and its subpackages instead of the virtual provides + (bsc#1258364). + +------------------------------------------------------------------- @@ -3235,5 +3293,0 @@ - -------------------------------------------------------------------- -Tue Jan 25 16:09:25 UTC 2022 - Matej Cepl <[email protected]> - -- Remove second superfluous BR rpm-build-python @@ -4707 +4761 @@ ------------------------------------------------------------------- +------------------------------------------------------------------- @@ -5066 +5120 @@ ------------------------------------------------------------------- +------------------------------------------------------------------- @@ -5917 +5970,0 @@ - Old: ---- macros.python3 New: ---- CVE-2026-1502-reject-CRLF-HTTP-tunnel.patch CVE-2026-3446-base64-padding.patch CVE-2026-4786-webbrowser-open-action.patch CVE-2026-6019-Morsel-js_output.patch CVE-2026-6100-use-after-free-decompression.patch ----------(New B)---------- New: (bsc#1261969, gh#python/cpython#146211) CVE-2026-1502-reject-CRLF-HTTP-tunnel.patch New: - Add CVE-2026-3446-base64-padding.patch preventing ignoring excess Base64 data after the first padded quad (bsc#1261970, New: dash-prefix check (bsc#1262319, gh#python/cpython#148169) CVE-2026-4786-webbrowser-open-action.patch New: gh#python/cpython#90309) CVE-2026-6019-Morsel-js_output.patch New: use-after-free error (bsc#1262098, gh#python/cpython#148395) CVE-2026-6100-use-after-free-decompression.patch ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python311.spec ++++++ --- /var/tmp/diff_new_pack.1IDvUO/_old 2026-06-19 17:22:09.427686167 +0200 +++ /var/tmp/diff_new_pack.1IDvUO/_new 2026-06-19 17:22:09.431686305 +0200 @@ -117,7 +117,6 @@ Source2: baselibs.conf Source3: README.SUSE Source4: externally_managed.in -Source7: macros.python3 Source8: import_failed.py Source9: import_failed.map Source10: pre_checkin.sh @@ -215,6 +214,21 @@ # PATCH-FIX-UPSTREAM CVE-2026-3479-pkgutil_get_data.patch bsc#1259989 [email protected] # pkgutil.get_data() reject invalid resource arguments Patch42: CVE-2026-3479-pkgutil_get_data.patch +# PATCH-FIX-UPSTREAM CVE-2026-3446-base64-padding.patch bsc#1261970 [email protected] +# Do not ignore excess Base64 data after the first padded quad +Patch43: CVE-2026-3446-base64-padding.patch +# PATCH-FIX-UPSTREAM CVE-2026-6100-use-after-free-decompression.patch bsc#1262098 [email protected] +# NULL dangling pointer to avoid use-after-free error +Patch44: CVE-2026-6100-use-after-free-decompression.patch +# PATCH-FIX-UPSTREAM CVE-2026-4786-webbrowser-open-action.patch bsc#1262319 [email protected] +# Fix webbrowser %action substitution bypass of dash-prefix check +Patch45: CVE-2026-4786-webbrowser-open-action.patch +# PATCH-FIX-UPSTREAM CVE-2026-1502-reject-CRLF-HTTP-tunnel.patch bsc#1261969 [email protected] +# Reject CR/LF in HTTP tunnel request headers +Patch46: CVE-2026-1502-reject-CRLF-HTTP-tunnel.patch +# PATCH-FIX-UPSTREAM CVE-2026-6019-Morsel-js_output.patch bsc#1262654 [email protected] +# Base64-encode cookie values embedded in JS +Patch47: CVE-2026-6019-Morsel-js_output.patch ### END OF PATCHES BuildRequires: autoconf-archive BuildRequires: automake @@ -232,8 +246,9 @@ BuildRequires: pkgconfig(uuid) BuildRequires: pkgconfig(zlib) #!BuildIgnore: gdk-pixbuf-loader-rsvg -%if 0%{?suse_version} >= 1550 -# The provider for python(abi) is in rpm-build-python +%if 0%{?suse_version} >= 1550 && %{without base} +# Skip for the base flavor: rpm-build-python requires python3-base, which +# creates an unresolvable dependency loop when building python3xx-base itself. BuildRequires: rpm-build-python %endif %if 0%{?suse_version} >= 1500 && 0%{?suse_version} < 1599 @@ -265,11 +280,6 @@ Recommends: %{python_pkg_name}-curses Recommends: %{python_pkg_name}-dbm Recommends: %{python_pkg_name}-pip -%if %{primary_interpreter} -Provides: python3 = %{python_version} -Provides: python3-readline -Provides: python3-sqlite3 -%endif %endif %{?suse_build_hwcaps_libs} @@ -290,9 +300,6 @@ %package -n %{python_pkg_name}-tk Summary: TkInter, a Python Tk Interface Requires: %{python_pkg_name} = %{version} -%if %{primary_interpreter} -Provides: python3-tk = %{version} -%endif %description -n %{python_pkg_name}-tk Python interface to Tk. Tk is the GUI toolkit that comes with Tcl. @@ -300,9 +307,6 @@ %package -n %{python_pkg_name}-curses Summary: Python Interface to the (N)Curses Library Requires: %{python_pkg_name} = %{version} -%if %{primary_interpreter} -Provides: python3-curses -%endif %description -n %{python_pkg_name}-curses An easy to use interface to the (n)curses CUI library. CUI stands for @@ -311,9 +315,6 @@ %package -n %{python_pkg_name}-dbm Summary: Python Interface to the GDBM Library Requires: %{python_pkg_name} = %{version} -%if %{primary_interpreter} -Provides: python3-dbm -%endif %description -n %{python_pkg_name}-dbm An easy to use interface for Unix DBM databases, and more specifically, @@ -323,9 +324,6 @@ Summary: An Integrated Development Environment for Python Requires: %{python_pkg_name} = %{version} Requires: %{python_pkg_name}-tk -%if %{primary_interpreter} -Provides: python3-idle = %{version} -%endif %description -n %{python_pkg_name}-idle IDLE is a Tkinter based integrated development environment for Python. @@ -336,9 +334,6 @@ %package -n %{python_pkg_name}-doc Summary: Package Documentation for Python 3 Enhances: %{python_pkg_name} = %{python_version} -%if %{primary_interpreter} -Provides: python3-doc = %{version} -%endif %description -n %{python_pkg_name}-doc Tutorial, Global Module Index, Language Reference, Library Reference, @@ -347,9 +342,6 @@ %package -n %{python_pkg_name}-doc-devhelp Summary: Additional Package Documentation for Python 3 in devhelp format -%if %{primary_interpreter} -Provides: python3-doc-devhelp = %{version} -%endif %description -n %{python_pkg_name}-doc-devhelp Tutorial, Global Module Index, Language Reference, Library Reference, @@ -371,16 +363,10 @@ Provides: %{python_pkg_name}-typing = %{version} # python3-xml was merged into python3, now moved into -base Provides: %{python_pkg_name}-xml = %{version} -%if %{primary_interpreter} -Provides: python3-asyncio = %{version} -Obsoletes: python3-asyncio < %{version} -Provides: python3-base = %{version} -Obsoletes: python3-base < %{version} -Provides: python3-typing = %{version} -Obsoletes: python3-typing < %{version} -Provides: python3-xml = %{version} -Obsoletes: python3-xml < %{version} -%endif +# Explicitly provided because rpm-build-python (which auto-generates this) +# cannot be installed in the base flavor build root due to a bootstrap cycle: +# rpm-build-python -> python3-base -> (this package) +Provides: python(abi) = %{python_version} %description -n %{python_pkg_name}-base Python is an interpreted, object-oriented programming language, and is @@ -398,13 +384,6 @@ Requires: %{python_pkg_name}-base = %{version} Provides: %{python_pkg_name}-2to3 = %{version} Provides: %{python_pkg_name}-demo = %{version} -%if %{primary_interpreter} -Provides: python3-2to3 = %{version} -Provides: python3-demo = %{version} -Provides: python3-tools = %{version} -Obsoletes: python3-2to3 < %{version} -Obsoletes: python3-demo < %{version} -%endif %description -n %{python_pkg_name}-tools A number of scripts that are useful for building, testing or extending Python, @@ -413,9 +392,6 @@ %package -n %{python_pkg_name}-devel Summary: Include Files and Libraries Mandatory for Building Python Modules Requires: %{python_pkg_name}-base = %{version} -%if %{primary_interpreter} -Provides: python3-devel = %{version} -%endif %description -n %{python_pkg_name}-devel The Python programming language's interpreter can be extended with @@ -432,9 +408,6 @@ Summary: Unit tests for Python and its standard library Requires: %{python_pkg_name} = %{version} Requires: %{python_pkg_name}-tk = %{version} -%if %{primary_interpreter} -Provides: python3-testsuite = %{version} -%endif %description -n %{python_pkg_name}-testsuite Unit tests that are useful for verifying integrity and functionality @@ -731,20 +704,12 @@ done rm -fv %{buildroot}%{dynlib nis} -# overwrite the copied binary with a link -ln -sf python%{python_version} %{buildroot}%{_bindir}/python3 - -# decide to ship python3 or just python3.X -%if !%{primary_interpreter} -# base rm %{buildroot}%{_bindir}/python3 rm %{buildroot}%{_bindir}/pydoc3 rm %{buildroot}%{_mandir}/man1/python3.1 -# devel rm %{buildroot}%{_bindir}/python3-config rm %{buildroot}%{_libdir}/libpython3.so rm %{buildroot}%{_libdir}/pkgconfig/{python3,python3-embed}.pc -%endif %if %{with externally_managed} # PEP-0668 mark this as a distro maintained python @@ -782,7 +747,13 @@ find "$PDOCS" -name "*.bat" -delete # put gdb helper script into place -install -m 755 -D Tools/gdb/libpython.py %{buildroot}%{_datadir}/gdb/auto-load/%{_libdir}/libpython%{python_abi}.so.%{so_major}.%{so_minor}-gdb.py +%define gdb_help_script libpython%{python_abi}.so.%{so_major}.%{so_minor}-gdb.py +install -m 755 -D Tools/gdb/libpython.py \ + %{buildroot}%{_datadir}/gdb/auto-load/%{_libdir}/%{gdb_help_script} +# don't use %python311_fix_shebang_path to avoid circular dependency via +# python-rpm-macros +sed -i "1s@#\!.*python[^ ]*@#\!%{_bindir}/python%{python_version}@" \ + %{buildroot}%{_datadir}/gdb/auto-load/%{_libdir}/%{gdb_help_script} # install devel files to /config #cp Makefile Makefile.pre.in Makefile.pre $RPM_BUILD_ROOT%%{sitedir}/config-%%{python_abi}/ @@ -790,12 +761,6 @@ # Remove -IVendor/ from python-config boo#1231795 sed -i 's/-IVendor\///' %{buildroot}%{_bindir}/python%{python_abi}-config -# RPM macros -%if %{primary_interpreter} -mkdir -p %{buildroot}%{_rpmconfigdir}/macros.d/ -install -m 644 %{SOURCE7} %{buildroot}%{_rpmconfigdir}/macros.d/ # macros.python3 -%endif - # import_failed hooks FAILDIR=%{buildroot}/%{sitedir}/_import_failed mkdir $FAILDIR @@ -899,16 +864,11 @@ %files -n %{python_pkg_name}-devel %{_libdir}/libpython%{python_abi}.so -%if %{primary_interpreter} -%{_libdir}/libpython3.so -%endif -%{_libdir}/pkgconfig/* +%{_libdir}/pkgconfig/python-%{python_version}.pc +%{_libdir}/pkgconfig/python-%{python_version}-embed.pc %{_includedir}/python%{python_abi} %{sitedir}/config-%{python_abi}-* %{_bindir}/python%{python_abi}-config -%if %{primary_interpreter} -%{_bindir}/python3-config -%endif # Own these directories to not depend on gdb %dir %{_datadir}/gdb %dir %{_datadir}/gdb/auto-load @@ -938,9 +898,6 @@ %doc %{_docdir}/%{name}/README.rst %license LICENSE %doc %{_docdir}/%{name}/README.SUSE -%if %{primary_interpreter} -%{_mandir}/man1/python3.1%{?ext_man} -%endif %{_mandir}/man1/python%{python_version}.1%{?ext_man} %if %{with externally_managed} # PEP-0668 @@ -948,10 +905,6 @@ %endif # license text, not a doc because the code can use it at run-time %{sitedir}/LICENSE.txt -# RPM macros -%if %{primary_interpreter} -%{_rpmconfigdir}/macros.d/macros.python3 -%endif # binary parts %dir %{sitedir}/lib-dynload %{dynlib array} @@ -1057,11 +1010,6 @@ # import-failed hooks %{sitedir}/_import_failed %{sitedir}/site-packages/zzzz-import-failed-hooks.pth -# symlinks -%if %{primary_interpreter} -%{_bindir}/python3 -%{_bindir}/pydoc3 -%endif # executables %attr(755, root, root) %{_bindir}/pydoc%{python_version} # %%attr(755, root, root) %%{_bindir}/python%%{python_abi} ++++++ CVE-2026-1502-reject-CRLF-HTTP-tunnel.patch ++++++ >From 043198392795df90c5ceddc8d1337448fd8bef6c Mon Sep 17 00:00:00 2001 From: Seth Larson <[email protected]> Date: Fri, 10 Apr 2026 10:21:42 -0500 Subject: [PATCH] [3.11] gh-146211: Reject CR/LF in HTTP tunnel request headers (GH-146212) (cherry picked from commit 05ed7ce7ae9e17c23a04085b2539fe6d6d3cef69) Co-authored-by: Seth Larson <[email protected]> Co-authored-by: Illia Volochii <[email protected]> --- Lib/http/client.py | 11 ++ Lib/test/test_httplib.py | 45 ++++++++++ Misc/NEWS.d/next/Security/2026-03-20-09-29-42.gh-issue-146211.PQVbs7.rst | 2 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Security/2026-03-20-09-29-42.gh-issue-146211.PQVbs7.rst Index: Python-3.11.15/Lib/http/client.py =================================================================== --- Python-3.11.15.orig/Lib/http/client.py 2026-04-25 19:06:55.324666120 +0200 +++ Python-3.11.15/Lib/http/client.py 2026-04-25 19:07:00.931284454 +0200 @@ -941,12 +941,21 @@ return ip def _tunnel(self): + if _contains_disallowed_url_pchar_re.search(self._tunnel_host): + raise ValueError('Tunnel host can\'t contain control characters %r' + % (self._tunnel_host,)) connect = b"CONNECT %s:%d HTTP/1.0\r\n" % ( self._wrap_ipv6(self._tunnel_host.encode("ascii")), self._tunnel_port) headers = [connect] for header, value in self._tunnel_headers.items(): - headers.append(f"{header}: {value}\r\n".encode("latin-1")) + header_bytes = header.encode("latin-1") + value_bytes = value.encode("latin-1") + if not _is_legal_header_name(header_bytes): + raise ValueError('Invalid header name %r' % (header_bytes,)) + if _is_illegal_header_value(value_bytes): + raise ValueError('Invalid header value %r' % (value_bytes,)) + headers.append(b"%s: %s\r\n" % (header_bytes, value_bytes)) headers.append(b"\r\n") # Making a single send() call instead of one per line encourages # the host OS to use a more optimal packet size instead of Index: Python-3.11.15/Lib/test/test_httplib.py =================================================================== --- Python-3.11.15.orig/Lib/test/test_httplib.py 2026-04-25 19:06:57.028324552 +0200 +++ Python-3.11.15/Lib/test/test_httplib.py 2026-04-25 19:07:00.932868704 +0200 @@ -371,6 +371,51 @@ with self.assertRaisesRegex(ValueError, 'Invalid header'): conn.putheader(name, value) + def test_invalid_tunnel_headers(self): + cases = ( + ('Invalid\r\nName', 'ValidValue'), + ('Invalid\rName', 'ValidValue'), + ('Invalid\nName', 'ValidValue'), + ('\r\nInvalidName', 'ValidValue'), + ('\rInvalidName', 'ValidValue'), + ('\nInvalidName', 'ValidValue'), + (' InvalidName', 'ValidValue'), + ('\tInvalidName', 'ValidValue'), + ('Invalid:Name', 'ValidValue'), + (':InvalidName', 'ValidValue'), + ('ValidName', 'Invalid\r\nValue'), + ('ValidName', 'Invalid\rValue'), + ('ValidName', 'Invalid\nValue'), + ('ValidName', 'InvalidValue\r\n'), + ('ValidName', 'InvalidValue\r'), + ('ValidName', 'InvalidValue\n'), + ) + for name, value in cases: + with self.subTest((name, value)): + conn = client.HTTPConnection('example.com') + conn.set_tunnel('tunnel', headers={ + name: value + }) + conn.sock = FakeSocket('') + with self.assertRaisesRegex(ValueError, 'Invalid header'): + conn._tunnel() # Called in .connect() + + def test_invalid_tunnel_host(self): + cases = ( + 'invalid\r.host', + '\ninvalid.host', + 'invalid.host\r\n', + 'invalid.host\x00', + 'invalid host', + ) + for tunnel_host in cases: + with self.subTest(tunnel_host): + conn = client.HTTPConnection('example.com') + conn.set_tunnel(tunnel_host) + conn.sock = FakeSocket('') + with self.assertRaisesRegex(ValueError, 'Tunnel host can\'t contain control characters'): + conn._tunnel() # Called in .connect() + def test_headers_debuglevel(self): body = ( b'HTTP/1.1 200 OK\r\n' Index: Python-3.11.15/Misc/NEWS.d/next/Security/2026-03-20-09-29-42.gh-issue-146211.PQVbs7.rst =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ Python-3.11.15/Misc/NEWS.d/next/Security/2026-03-20-09-29-42.gh-issue-146211.PQVbs7.rst 2026-04-25 19:07:00.933147420 +0200 @@ -0,0 +1,2 @@ +Reject CR/LF characters in tunnel request headers for the +HTTPConnection.set_tunnel() method. ++++++ CVE-2026-3446-base64-padding.patch ++++++ >From bd4ab523ba664863d40470cc718c566158adfa31 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka <[email protected]> Date: Tue, 24 Mar 2026 01:20:26 +0200 Subject: [PATCH] [3.14] gh-145264: Do not ignore excess Base64 data after the first padded quad (GH-145267) (GH-146326) Base64 decoder (see binascii.a2b_base64(), base64.b64decode(), etc) no longer ignores excess data after the first padded quad in non-strict (default) mode. Instead, in conformance with RFC 4648, it ignores the pad character, "=", if it is present before the end of the encoded data. (cherry picked from commit 4561f6418a691b3e89aef0901f53fe0dfb7f7c0e) (cherry picked from commit e31c55121620189a0d1a07b689762d8ca9c1b7fa) Co-authored-by: Serhiy Storchaka <[email protected]> --- Lib/test/test_binascii.py | 33 +++ Misc/NEWS.d/next/Library/2026-02-26-20-13-16.gh-issue-145264.4pggX_.rst | 4 Modules/binascii.c | 90 +++++----- 3 files changed, 78 insertions(+), 49 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-02-26-20-13-16.gh-issue-145264.4pggX_.rst Index: Python-3.11.15/Lib/test/test_binascii.py =================================================================== --- Python-3.11.15.orig/Lib/test/test_binascii.py 2026-04-16 01:25:00.019387400 +0200 +++ Python-3.11.15/Lib/test/test_binascii.py 2026-04-16 01:37:02.691981027 +0200 @@ -132,13 +132,20 @@ def assertDiscontinuousPadding(data, non_strict_mode_expected_result: bytes): _assertRegexTemplate(r'(?i)Discontinuous padding', data, non_strict_mode_expected_result) + def assertExcessPadding(data, non_strict_mode_expected_result: bytes): + _assertRegexTemplate(r'(?i)Excess padding', data, non_strict_mode_expected_result) + # Test excess data exceptions - assertExcessData(b'ab==a', b'i') - assertExcessData(b'ab===', b'i') - assertExcessData(b'ab==:', b'i') - assertExcessData(b'abc=a', b'i\xb7') - assertExcessData(b'abc=:', b'i\xb7') - assertExcessData(b'ab==\n', b'i') + assertExcessPadding(b'ab===', b'i') + assertExcessPadding(b'ab====', b'i') + assertNonBase64Data(b'ab==:', b'i') + assertExcessData(b'abc=a', b'i\xb7\x1a') + assertNonBase64Data(b'abc=:', b'i\xb7') + assertNonBase64Data(b'ab==\n', b'i') + assertExcessPadding(b'abc==', b'i\xb7') + assertExcessPadding(b'abc===', b'i\xb7') + assertExcessPadding(b'abc====', b'i\xb7') + assertExcessPadding(b'abc=====', b'i\xb7') # Test non-base64 data exceptions assertNonBase64Data(b'\nab==', b'i') @@ -153,6 +160,20 @@ assertDiscontinuousPadding(b'ab=c=', b'i\xb7') assertDiscontinuousPadding(b'ab=ab==', b'i\xb6\x9b') + def test_base64_excess_data(self): + # Test excess data exceptions + def assertExcessData(data, expected): + assert_regex = r'(?i)Excess data' + data = self.type2test(data) + with self.assertRaisesRegex(binascii.Error, assert_regex): + binascii.a2b_base64(data, strict_mode=True) + self.assertEqual(binascii.a2b_base64(data, strict_mode=False), + expected) + self.assertEqual(binascii.a2b_base64(data), expected) + + assertExcessData(b'ab==c=', b'i\xb7') + assertExcessData(b'ab==cd', b'i\xb7\x1d') + assertExcessData(b'abc=d', b'i\xb7\x1d') def test_base64errors(self): # Test base64 with invalid padding Index: Python-3.11.15/Misc/NEWS.d/next/Library/2026-02-26-20-13-16.gh-issue-145264.4pggX_.rst =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ Python-3.11.15/Misc/NEWS.d/next/Library/2026-02-26-20-13-16.gh-issue-145264.4pggX_.rst 2026-04-16 01:25:07.716277511 +0200 @@ -0,0 +1,4 @@ +Base64 decoder (see :func:`binascii.a2b_base64`, :func:`base64.b64decode`, etc) no +longer ignores excess data after the first padded quad in non-strict +(default) mode. Instead, in conformance with :rfc:`4648`, section 3.3, it now ignores +the pad character, "=", if it is present before the end of the encoded data. Index: Python-3.11.15/Modules/binascii.c =================================================================== --- Python-3.11.15.orig/Modules/binascii.c 2026-03-03 01:52:57.000000000 +0100 +++ Python-3.11.15/Modules/binascii.c 2026-04-16 01:33:46.756241326 +0200 @@ -393,7 +393,6 @@ const unsigned char *ascii_data = data->buf; size_t ascii_len = data->len; binascii_state *state = NULL; - char padding_started = 0; /* Allocate the buffer */ Py_ssize_t bin_len = ((ascii_len+3)/4)*3; /* Upper bound, corrected later */ @@ -404,14 +403,6 @@ return NULL; unsigned char *bin_data_start = bin_data; - if (strict_mode && ascii_len > 0 && ascii_data[0] == '=') { - state = get_binascii_state(module); - if (state) { - PyErr_SetString(state->Error, "Leading padding not allowed"); - } - goto error_end; - } - int quad_pos = 0; unsigned char leftchar = 0; int pads = 0; @@ -422,28 +413,34 @@ ** the invalid ones. */ if (this_ch == BASE64_PAD) { - padding_started = 1; - - if (quad_pos >= 2 && quad_pos + ++pads >= 4) { - /* A pad sequence means we should not parse more input. - ** We've already interpreted the data from the quad at this point. - ** in strict mode, an error should raise if there's excess data after the padding. - */ - if (strict_mode && i + 1 < ascii_len) { - state = get_binascii_state(module); - if (state) { - PyErr_SetString(state->Error, "Excess data after padding"); - } - goto error_end; - } - - goto done; + pads++; + if (quad_pos >= 2 && quad_pos + pads <= 4) { + continue; } - continue; + // See RFC 4648, section-3.3: "specifications MAY ignore the + // pad character, "=", treating it as non-alphabet data, if + // it is present before the end of the encoded data" and + // "the excess pad characters MAY also be ignored." + if (!strict_mode) { + continue; + } + if (quad_pos == 1) { + /* Set an error below. */ + break; + } + state = get_binascii_state(module); + if (state) { + PyErr_SetString(state->Error, + (quad_pos == 0 && i == 0) + ? "Leading padding not allowed" + : "Excess padding not allowed"); + } + goto error_end; } this_ch = table_a2b_base64[this_ch]; if (this_ch >= 64) { + // See RFC 4648, section-3.3. if (strict_mode) { state = get_binascii_state(module); if (state) { @@ -454,11 +451,14 @@ continue; } - // Characters that are not '=', in the middle of the padding, are not allowed - if (strict_mode && padding_started) { + // Characters that are not '=', in the middle of the padding, are + // not allowed (except when they are). See RFC 4648, section-3.3. + if (pads && strict_mode) { state = get_binascii_state(module); if (state) { - PyErr_SetString(state->Error, "Discontinuous padding not allowed"); + PyErr_SetString(state->Error, (quad_pos + pads == 4) + ? "Excess data after padding" + : "Discontinuous padding not allowed"); } goto error_end; } @@ -487,31 +487,35 @@ } } - if (quad_pos != 0) { + if (quad_pos == 1) { + /* There is exactly one extra valid, non-padding, base64 character. + * * This is an invalid length, as there is no possible input that + ** could encoded into such a base64 string. + */ state = get_binascii_state(module); - if (state == NULL) { - /* error already set, from get_binascii_state */ - } else if (quad_pos == 1) { - /* - ** There is exactly one extra valid, non-padding, base64 character. - ** This is an invalid length, as there is no possible input that - ** could encoded into such a base64 string. - */ + if (state) { PyErr_Format(state->Error, "Invalid base64-encoded string: " "number of data characters (%zd) cannot be 1 more " "than a multiple of 4", (bin_data - bin_data_start) / 3 * 4 + 1); - } else { + } + goto error_end; + } + + if (quad_pos != 0 && quad_pos + pads < 4) { + state = get_binascii_state(module); + if (state) { PyErr_SetString(state->Error, "Incorrect padding"); } - error_end: - _PyBytesWriter_Dealloc(&writer); - return NULL; + goto error_end; } -done: return _PyBytesWriter_Finish(&writer, bin_data); + +error_end: + _PyBytesWriter_Dealloc(&writer); + return NULL; } ++++++ CVE-2026-4786-webbrowser-open-action.patch ++++++ >From d57576d12bdcd9d86d527d81241e7faaa05c972c Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <[email protected]> Date: Mon, 13 Apr 2026 20:02:52 +0100 Subject: [PATCH] [3.11] gh-148169: Fix webbrowser `%action` substitution bypass of dash-prefix check (GH-148170) (cherry picked from commit d22922c8a7958353689dc4763dd72da2dea03fff) Co-authored-by: Stan Ulbrych <[email protected]> --- Lib/test/test_webbrowser.py | 8 ++++++++ Lib/webbrowser.py | 5 +++-- Misc/NEWS.d/next/Security/2026-03-31-09-15-51.gh-issue-148169.EZJzz2.rst | 2 ++ 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2026-03-31-09-15-51.gh-issue-148169.EZJzz2.rst Index: Python-3.11.15/Lib/test/test_webbrowser.py =================================================================== --- Python-3.11.15.orig/Lib/test/test_webbrowser.py 2026-04-25 12:59:46.688833230 +0200 +++ Python-3.11.15/Lib/test/test_webbrowser.py 2026-04-25 12:59:46.742650302 +0200 @@ -99,6 +99,14 @@ options=[], arguments=[URL]) + def test_reject_action_dash_prefixes(self): + browser = self.browser_class(name=CMD_NAME) + with self.assertRaises(ValueError): + browser.open('%action--incognito') + # new=1: action is "--new-window", so "%action" itself expands to + # a dash-prefixed flag even with no dash in the original URL. + with self.assertRaises(ValueError): + browser.open('%action', new=1) class MozillaCommandTest(CommandTestMixin, unittest.TestCase): Index: Python-3.11.15/Lib/webbrowser.py =================================================================== --- Python-3.11.15.orig/Lib/webbrowser.py 2026-04-25 12:59:46.689165976 +0200 +++ Python-3.11.15/Lib/webbrowser.py 2026-04-25 12:59:46.742825301 +0200 @@ -264,7 +264,6 @@ def open(self, url, new=0, autoraise=True): sys.audit("webbrowser.open", url) - self._check_url(url) if new == 0: action = self.remote_action elif new == 1: @@ -278,7 +277,9 @@ raise Error("Bad 'new' parameter to open(); " + "expected 0, 1, or 2, got %s" % new) - args = [arg.replace("%s", url).replace("%action", action) + self._check_url(url.replace("%action", action)) + + args = [arg.replace("%action", action).replace("%s", url) for arg in self.remote_args] args = [arg for arg in args if arg] success = self._invoke(args, True, autoraise, url) Index: Python-3.11.15/Misc/NEWS.d/next/Security/2026-03-31-09-15-51.gh-issue-148169.EZJzz2.rst =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ Python-3.11.15/Misc/NEWS.d/next/Security/2026-03-31-09-15-51.gh-issue-148169.EZJzz2.rst 2026-04-25 12:59:46.743018775 +0200 @@ -0,0 +1,2 @@ +A bypass in :mod:`webbrowser` allowed URLs prefixed with ``%action`` to pass +the dash-prefix safety check. ++++++ CVE-2026-6019-Morsel-js_output.patch ++++++ >From a67e6c856353c04782f38bca6d6c1c3d3287c653 Mon Sep 17 00:00:00 2001 From: Seth Larson <[email protected]> Date: Wed, 22 Apr 2026 14:22:31 -0500 Subject: [PATCH] gh-90309: Base64-encode cookie values embedded in JS (cherry picked from commit 76b3923d688c0efc580658476c5f525ec8735104) Co-authored-by: Seth Larson <[email protected]> --- Lib/http/cookies.py | 8 ++ Lib/test/test_http_cookies.py | 29 ++++++---- Misc/NEWS.d/next/Security/2026-04-21-13-46-30.gh-issue-90309.srvj9q.rst | 3 + 3 files changed, 27 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2026-04-21-13-46-30.gh-issue-90309.srvj9q.rst Index: Python-3.11.15/Lib/http/cookies.py =================================================================== --- Python-3.11.15.orig/Lib/http/cookies.py 2026-04-27 18:46:03.982786002 +0200 +++ Python-3.11.15/Lib/http/cookies.py 2026-04-27 18:46:04.117463904 +0200 @@ -389,17 +389,21 @@ return '<%s: %s>' % (self.__class__.__name__, self.OutputString()) def js_output(self, attrs=None): + import base64 # Print javascript output_string = self.OutputString(attrs) if _has_control_character(output_string): raise CookieError("Control characters are not allowed in cookies") + # Base64-encode value to avoid template + # injection in cookie values. + output_encoded = base64.b64encode(output_string.encode('utf-8')).decode("ascii") return """ <script type="text/javascript"> <!-- begin hiding - document.cookie = \"%s\"; + document.cookie = atob(\"%s\"); // end hiding --> </script> - """ % (output_string.replace('"', r'\"')) + """ % (output_encoded,) def OutputString(self, attrs=None): # Build up our result Index: Python-3.11.15/Lib/test/test_http_cookies.py =================================================================== --- Python-3.11.15.orig/Lib/test/test_http_cookies.py 2026-04-27 18:46:03.983146511 +0200 +++ Python-3.11.15/Lib/test/test_http_cookies.py 2026-04-27 18:46:04.118250229 +0200 @@ -1,5 +1,5 @@ # Simple test suite for http/cookies.py - +import base64 import copy import unittest import doctest @@ -106,17 +106,19 @@ self.assertEqual(C.output(['path']), 'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme') - self.assertEqual(C.js_output(), r""" + cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE"; Path=/acme; Version=1').decode('ascii') + self.assertEqual(C.js_output(), fr""" <script type="text/javascript"> <!-- begin hiding - document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1"; + document.cookie = atob("{cookie_encoded}"); // end hiding --> </script> """) - self.assertEqual(C.js_output(['path']), r""" + cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE"; Path=/acme').decode('ascii') + self.assertEqual(C.js_output(['path']), fr""" <script type="text/javascript"> <!-- begin hiding - document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme"; + document.cookie = atob("{cookie_encoded}"); // end hiding --> </script> """) @@ -213,17 +215,19 @@ self.assertEqual(C.output(['path']), 'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme') - self.assertEqual(C.js_output(), r""" + expected_encoded_cookie = base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1').decode('ascii') + self.assertEqual(C.js_output(), fr""" <script type="text/javascript"> <!-- begin hiding - document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1"; + document.cookie = atob("{expected_encoded_cookie}"); // end hiding --> </script> """) - self.assertEqual(C.js_output(['path']), r""" + expected_encoded_cookie = base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme').decode('ascii') + self.assertEqual(C.js_output(['path']), fr""" <script type="text/javascript"> <!-- begin hiding - document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme"; + document.cookie = atob("{expected_encoded_cookie}"); // end hiding --> </script> """) @@ -314,13 +318,16 @@ self.assertEqual( M.output(), "Set-Cookie: %s=%s; Path=/foo" % (i, "%s_coded_val" % i)) + expected_encoded_cookie = base64.b64encode( + ("%s=%s; Path=/foo" % (i, "%s_coded_val" % i)).encode("ascii") + ).decode('ascii') expected_js_output = """ <script type="text/javascript"> <!-- begin hiding - document.cookie = "%s=%s; Path=/foo"; + document.cookie = atob("%s"); // end hiding --> </script> - """ % (i, "%s_coded_val" % i) + """ % (expected_encoded_cookie,) self.assertEqual(M.js_output(), expected_js_output) for i in ["foo bar", "foo@bar"]: # Try some illegal characters Index: Python-3.11.15/Misc/NEWS.d/next/Security/2026-04-21-13-46-30.gh-issue-90309.srvj9q.rst =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ Python-3.11.15/Misc/NEWS.d/next/Security/2026-04-21-13-46-30.gh-issue-90309.srvj9q.rst 2026-04-27 18:46:04.118574830 +0200 @@ -0,0 +1,3 @@ +Base64-encode values when embedding cookies to JavaScript using the +:meth:`http.cookies.BaseCookie.js_output` method to avoid injection +and escaping. ++++++ CVE-2026-6100-use-after-free-decompression.patch ++++++ >From 72d9d77abbb4a30885d244c4faf963082e0c1b24 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <[email protected]> Date: Mon, 13 Apr 2026 02:14:54 +0100 Subject: [PATCH 1/2] [3.11] gh-148395: Fix a possible UAF in `{LZMA,BZ2,_Zlib}Decompressor` (GH-148396) Fix dangling input pointer after `MemoryError` in _lzma/_bz2/_ZlibDecompressor.decompress (cherry picked from commit 8fc66aef6d7b3ae58f43f5c66f9366cc8cbbfcd2) Co-authored-by: Stan Ulbrych <[email protected]> --- Misc/NEWS.d/next/Security/2026-04-10-16-28-21.gh-issue-148395.kfzm0G.rst | 5 +++++ Modules/_bz2module.c | 1 + Modules/_lzmamodule.c | 1 + 3 files changed, 7 insertions(+) create mode 100644 Misc/NEWS.d/next/Security/2026-04-10-16-28-21.gh-issue-148395.kfzm0G.rst Index: Python-3.11.15/Misc/NEWS.d/next/Security/2026-04-10-16-28-21.gh-issue-148395.kfzm0G.rst =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ Python-3.11.15/Misc/NEWS.d/next/Security/2026-04-10-16-28-21.gh-issue-148395.kfzm0G.rst 2026-04-24 23:04:46.062939247 +0200 @@ -0,0 +1,5 @@ +Fix a dangling input pointer in :class:`lzma.LZMADecompressor`, +and :class:`bz2.BZ2Decompressor` +when memory allocation fails with :exc:`MemoryError`, which could let a +subsequent :meth:`!decompress` call read or write through a stale pointer to +the already-released caller buffer. Index: Python-3.11.15/Modules/_bz2module.c =================================================================== --- Python-3.11.15.orig/Modules/_bz2module.c 2026-03-03 01:52:57.000000000 +0100 +++ Python-3.11.15/Modules/_bz2module.c 2026-04-24 23:04:46.062526709 +0200 @@ -595,6 +595,7 @@ return result; error: + bzs->next_in = NULL; Py_XDECREF(result); return NULL; } Index: Python-3.11.15/Modules/_lzmamodule.c =================================================================== --- Python-3.11.15.orig/Modules/_lzmamodule.c 2026-03-03 01:52:57.000000000 +0100 +++ Python-3.11.15/Modules/_lzmamodule.c 2026-04-24 23:04:46.062750672 +0200 @@ -1105,6 +1105,7 @@ return result; error: + lzs->next_in = NULL; Py_XDECREF(result); return NULL; } ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.1IDvUO/_old 2026-06-19 17:22:09.639693459 +0200 +++ /var/tmp/diff_new_pack.1IDvUO/_new 2026-06-19 17:22:09.643693596 +0200 @@ -1,6 +1,6 @@ -mtime: 1775598908 -commit: 74faae37ad8780b94fb71dd3921b3e672ecda40ae693a97c1dcdcfce5bf3b46e -url: https://src.opensuse.org/python-interpreters/python311.git -revision: 74faae37ad8780b94fb71dd3921b3e672ecda40ae693a97c1dcdcfce5bf3b46e +mtime: 1780778098 +commit: 40ae616787d508cbe0e1213b14035fad0a6bc7a89e895ab247b215e6cb3dd4cb +url: https://src.opensuse.org/python-interpreters/python311 +revision: 40ae616787d508cbe0e1213b14035fad0a6bc7a89e895ab247b215e6cb3dd4cb projectscmsync: https://src.opensuse.org/python-interpreters/_ObsPrj ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-06-06 22:34:58.000000000 +0200 @@ -0,0 +1,7 @@ +_build.* +*.obscpio +*.osc +.osc +.pbuild +python311-*-build/ +*.rej ++++++ python311-rpmlintrc ++++++ --- /var/tmp/diff_new_pack.1IDvUO/_old 2026-06-19 17:22:09.975705014 +0200 +++ /var/tmp/diff_new_pack.1IDvUO/_new 2026-06-19 17:22:09.983705289 +0200 @@ -1,5 +1,5 @@ -addFilter("pem-certificate.*/usr/lib.*/python.*/test/*.pem") -addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/tests/*.c") -addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/test/*.cpp") +addFilter("pem-certificate.*/usr/lib.*/python.*/test/.*.pem") +addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/tests/.*.c") +addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/test/.*.cpp") addFilter("python-bytecode-inconsistent-mtime.*\.pyc")
