Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.debian.bugs.dist > #1292600
| From | Arnaud Rebillout <arnaudr@debian.org> |
|---|---|
| Newsgroups | linux.debian.bugs.dist, linux.debian.devel.release |
| Subject | Bug#1135877: trixie-pu: package python3.13/3.13.5-2+deb13u2 |
| Date | 2026-05-07 11:30 +0200 |
| Message-ID | <MS3do-3f68-5@gated-at.bofh.it> (permalink) |
| References | <MRRvA-36qX-3@gated-at.bofh.it> <MRRvA-36qX-3@gated-at.bofh.it> |
| Organization | linux.* mail to news gateway |
Cross-posted to 2 groups.
Hello Moritz,
I intend to propose a similar upload for bookworm, so I was looking at
your debdiff first.
May I ask: why not fixing CVE-2026-4786 as well? It is marked as fixed
in the tracker, but it's only because it is introduced by the fix
for CVE-2026-4519, which is not yet in trixie (it's in your debdiff here).
Best,
Arnaud
On 07/05/2026 03:50, Moritz Muehlenhoff wrote:
> Package: release.debian.org
> Severity: normal
> Tags: trixie
> X-Debbugs-Cc: python3.13@packages.debian.org, doko@debian.org
> Control: affects -1 + src:python3.13
> User: release.debian.org@packages.debian.org
> Usertags: pu
>
> This is a followup update to the +deb13u1 update already
> accepeted to trixie-proposed-updates a few weeks ago,
> which fixes a more low severity security issues, which
> have since then been backported to the upstream 3.13
> branch.
>
> Tests were fine and everything looks in order in the tests
> triggered in debusine as well. The debdiff below is relative
> to the version already on trixie-p-u.
>
> Cheers,
> Moritz
>
> diff -Nru python3.13-3.13.5/debian/changelog python3.13-3.13.5/debian/changelog
> --- python3.13-3.13.5/debian/changelog 2026-04-06 14:24:14.000000000 +0200
> +++ python3.13-3.13.5/debian/changelog 2026-05-05 23:05:52.000000000 +0200
> @@ -1,3 +1,14 @@
> +python3.13 (3.13.5-2+deb13u2) trixie; urgency=medium
> +
> + * CVE-2026-3446
> + * CVE-2026-4224
> + * CVE-2026-3644
> + * CVE-2026-4519
> + * CVE-2026-6019 (Closes: #1135116)
> + * CVE-2026-6100
> +
> + -- Moritz Mühlenhoff <jmm@debian.org> Tue, 05 May 2026 23:05:52 +0200
> +
> python3.13 (3.13.5-2+deb13u1) trixie; urgency=medium
>
> * CVE-2025-11468 (Closes: #1126787)
> diff -Nru python3.13-3.13.5/debian/patches/CVE-2026-3446.patch python3.13-3.13.5/debian/patches/CVE-2026-3446.patch
> --- python3.13-3.13.5/debian/patches/CVE-2026-3446.patch 1970-01-01 01:00:00.000000000 +0100
> +++ python3.13-3.13.5/debian/patches/CVE-2026-3446.patch 2026-05-05 14:21:14.000000000 +0200
> @@ -0,0 +1,209 @@
> +From 1f9958f909c1b41a4ffc0b613ef8ec8fa5e7c474 Mon Sep 17 00:00:00 2001
> +From: "Miss Islington (bot)"
> + <31488909+miss-islington@users.noreply.github.com>
> +Date: Tue, 24 Mar 2026 00:52:20 +0100
> +Subject: [PATCH] [3.13] gh-145264: Do not ignore excess Base64 data after the
> + first padded quad (GH-145267) (GH-146326) (GH-146348)
> +
> +--- python3.13-3.13.5.orig/Lib/test/test_binascii.py
> ++++ python3.13-3.13.5/Lib/test/test_binascii.py
> +@@ -143,17 +143,16 @@ class BinASCIITest(unittest.TestCase):
> + _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'ab==:', b'i')
> +- assertExcessData(b'abc=a', b'i\xb7')
> +- assertExcessData(b'abc=:', b'i\xb7')
> +- assertExcessData(b'ab==\n', b'i')
> +- assertExcessData(b'abc==', b'i\xb7')
> +- assertExcessData(b'abc===', b'i\xb7')
> +- assertExcessData(b'abc====', b'i\xb7')
> +- assertExcessData(b'abc=====', b'i\xb7')
> ++ 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')
> +@@ -175,6 +174,20 @@ class BinASCIITest(unittest.TestCase):
> + assertExcessPadding(b'abcd====', b'i\xb7\x1d')
> + assertExcessPadding(b'abcd=====', b'i\xb7\x1d')
> +
> ++ 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
> +--- python3.13-3.13.5.orig/Modules/binascii.c
> ++++ python3.13-3.13.5/Modules/binascii.c
> +@@ -383,7 +383,6 @@ binascii_a2b_base64_impl(PyObject *modul
> + 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 */
> +@@ -394,14 +393,6 @@ binascii_a2b_base64_impl(PyObject *modul
> + 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;
> +@@ -412,35 +403,34 @@ binascii_a2b_base64_impl(PyObject *modul
> + ** the invalid ones.
> + */
> + if (this_ch == BASE64_PAD) {
> +- padding_started = 1;
> +-
> +- if (strict_mode && quad_pos == 0) {
> +- state = get_binascii_state(module);
> +- if (state) {
> +- PyErr_SetString(state->Error, "Excess padding not allowed");
> +- }
> +- goto error_end;
> ++ pads++;
> ++ if (quad_pos >= 2 && quad_pos + pads <= 4) {
> ++ continue;
> + }
> +- 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;
> ++ // 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;
> + }
> +- 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) {
> +@@ -451,11 +441,14 @@ binascii_a2b_base64_impl(PyObject *modul
> + 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;
> + }
> +@@ -484,31 +477,35 @@ binascii_a2b_base64_impl(PyObject *modul
> + }
> + }
> +
> +- 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;
> + }
> +
> +
> diff -Nru python3.13-3.13.5/debian/patches/CVE-2026-3644.patch python3.13-3.13.5/debian/patches/CVE-2026-3644.patch
> --- python3.13-3.13.5/debian/patches/CVE-2026-3644.patch 1970-01-01 01:00:00.000000000 +0100
> +++ python3.13-3.13.5/debian/patches/CVE-2026-3644.patch 2026-05-05 23:01:49.000000000 +0200
> @@ -0,0 +1,124 @@
> +From d16ecc6c3626f0e2cc8f08c309c83934e8a979dd Mon Sep 17 00:00:00 2001
> +From: "Miss Islington (bot)"
> + <31488909+miss-islington@users.noreply.github.com>
> +Date: Mon, 16 Mar 2026 15:05:13 +0100
> +Subject: [PATCH] [3.13] gh-145599, CVE 2026-3644: Reject control characters in
> + `http.cookies.Morsel.update()` (GH-145600) (#146024)
> +
> +--- python3.13-3.13.5.orig/Lib/http/cookies.py
> ++++ python3.13-3.13.5/Lib/http/cookies.py
> +@@ -335,9 +335,16 @@ class Morsel(dict):
> + key = key.lower()
> + if key not in self._reserved:
> + raise CookieError("Invalid attribute %r" % (key,))
> ++ if _has_control_character(key, val):
> ++ raise CookieError("Control characters are not allowed in "
> ++ f"cookies {key!r} {val!r}")
> + data[key] = val
> + dict.update(self, data)
> +
> ++ def __ior__(self, values):
> ++ self.update(values)
> ++ return self
> ++
> + def isReservedKey(self, K):
> + return K.lower() in self._reserved
> +
> +@@ -363,9 +370,15 @@ class Morsel(dict):
> + }
> +
> + def __setstate__(self, state):
> +- self._key = state['key']
> +- self._value = state['value']
> +- self._coded_value = state['coded_value']
> ++ key = state['key']
> ++ value = state['value']
> ++ coded_value = state['coded_value']
> ++ if _has_control_character(key, value, coded_value):
> ++ raise CookieError("Control characters are not allowed in cookies "
> ++ f"{key!r} {value!r} {coded_value!r}")
> ++ self._key = key
> ++ self._value = value
> ++ self._coded_value = coded_value
> +
> + def output(self, attrs=None, header="Set-Cookie:"):
> + return "%s %s" % (header, self.OutputString(attrs))
> +@@ -377,13 +390,16 @@ class Morsel(dict):
> +
> + def js_output(self, attrs=None):
> + # Print javascript
> ++ output_string = self.OutputString(attrs)
> ++ if _has_control_character(output_string):
> ++ raise CookieError("Control characters are not allowed in cookies")
> + return """
> + <script type="text/javascript">
> + <!-- begin hiding
> + document.cookie = \"%s\";
> + // end hiding -->
> + </script>
> +- """ % (self.OutputString(attrs).replace('"', r'\"'))
> ++ """ % (output_string.replace('"', r'\"'))
> +
> + def OutputString(self, attrs=None):
> + # Build up our result
> +--- python3.13-3.13.5.orig/Lib/test/test_http_cookies.py
> ++++ python3.13-3.13.5/Lib/test/test_http_cookies.py
> +@@ -574,6 +574,14 @@ class MorselTests(unittest.TestCase):
> + with self.assertRaises(cookies.CookieError):
> + morsel["path"] = c0
> +
> ++ # .__setstate__()
> ++ with self.assertRaises(cookies.CookieError):
> ++ morsel.__setstate__({'key': c0, 'value': 'val', 'coded_value': 'coded'})
> ++ with self.assertRaises(cookies.CookieError):
> ++ morsel.__setstate__({'key': 'key', 'value': c0, 'coded_value': 'coded'})
> ++ with self.assertRaises(cookies.CookieError):
> ++ morsel.__setstate__({'key': 'key', 'value': 'val', 'coded_value': c0})
> ++
> + # .setdefault()
> + with self.assertRaises(cookies.CookieError):
> + morsel.setdefault("path", c0)
> +@@ -588,6 +596,18 @@ class MorselTests(unittest.TestCase):
> + with self.assertRaises(cookies.CookieError):
> + morsel.set("path", "val", c0)
> +
> ++ # .update()
> ++ with self.assertRaises(cookies.CookieError):
> ++ morsel.update({"path": c0})
> ++ with self.assertRaises(cookies.CookieError):
> ++ morsel.update({c0: "val"})
> ++
> ++ # .__ior__()
> ++ with self.assertRaises(cookies.CookieError):
> ++ morsel |= {"path": c0}
> ++ with self.assertRaises(cookies.CookieError):
> ++ morsel |= {c0: "val"}
> ++
> + def test_control_characters_output(self):
> + # Tests that even if the internals of Morsel are modified
> + # that a call to .output() has control character safeguards.
> +@@ -608,6 +628,24 @@ class MorselTests(unittest.TestCase):
> + with self.assertRaises(cookies.CookieError):
> + cookie.output()
> +
> ++ # Tests that .js_output() also has control character safeguards.
> ++ for c0 in support.control_characters_c0():
> ++ morsel = cookies.Morsel()
> ++ morsel.set("key", "value", "coded-value")
> ++ morsel._key = c0 # Override private variable.
> ++ cookie = cookies.SimpleCookie()
> ++ cookie["cookie"] = morsel
> ++ with self.assertRaises(cookies.CookieError):
> ++ cookie.js_output()
> ++
> ++ morsel = cookies.Morsel()
> ++ morsel.set("key", "value", "coded-value")
> ++ morsel._coded_value = c0 # Override private variable.
> ++ cookie = cookies.SimpleCookie()
> ++ cookie["cookie"] = morsel
> ++ with self.assertRaises(cookies.CookieError):
> ++ cookie.js_output()
> ++
> +
> + def load_tests(loader, tests, pattern):
> + tests.addTest(doctest.DocTestSuite(cookies))
> diff -Nru python3.13-3.13.5/debian/patches/CVE-2026-4224.patch python3.13-3.13.5/debian/patches/CVE-2026-4224.patch
> --- python3.13-3.13.5/debian/patches/CVE-2026-4224.patch 1970-01-01 01:00:00.000000000 +0100
> +++ python3.13-3.13.5/debian/patches/CVE-2026-4224.patch 2026-05-05 14:48:22.000000000 +0200
> @@ -0,0 +1,71 @@
> +From 196edfb06a7458377d4d0f4b3cd41724c1f3bd4a Mon Sep 17 00:00:00 2001
> +From: "Miss Islington (bot)"
> + <31488909+miss-islington@users.noreply.github.com>
> +Date: Mon, 16 Mar 2026 10:09:27 +0100
> +Subject: [PATCH] [3.13] gh-145986: Avoid unbound C recursion in
> + `conv_content_model` in `pyexpat.c` (CVE 2026-4224) (GH-145987) (#145996)
> +
> +--- python3.13-3.13.5.orig/Lib/test/test_pyexpat.py
> ++++ python3.13-3.13.5/Lib/test/test_pyexpat.py
> +@@ -668,6 +668,22 @@ class ChardataBufferTest(unittest.TestCa
> + parser.Parse(xml2, True)
> + self.assertEqual(self.n, 4)
> +
> ++ def test_deeply_nested_content_model(self):
> ++ # This should raise a RecursionError and not crash.
> ++ # See https://github.com/python/cpython/issues/145986.
> ++ N = 500_000
> ++ data = (
> ++ b'<!DOCTYPE root [\n<!ELEMENT root '
> ++ + b'(a, ' * N + b'a' + b')' * N
> ++ + b'>\n]>\n<root/>\n'
> ++ )
> ++
> ++ parser = expat.ParserCreate()
> ++ parser.ElementDeclHandler = lambda _1, _2: None
> ++ with support.infinite_recursion():
> ++ with self.assertRaises(RecursionError):
> ++ parser.Parse(data)
> ++
> + class MalformedInputTest(unittest.TestCase):
> + def test1(self):
> + xml = b"\0\r\n"
> +--- python3.13-3.13.5.orig/Modules/pyexpat.c
> ++++ python3.13-3.13.5/Modules/pyexpat.c
> +@@ -3,6 +3,7 @@
> + #endif
> +
> + #include "Python.h"
> ++#include "pycore_ceval.h" // _Py_EnterRecursiveCall()
> + #include "pycore_import.h" // _PyImport_SetModule()
> + #include "pycore_pyhash.h" // _Py_HashSecret
> + #include "pycore_traceback.h" // _PyTraceback_Add()
> +@@ -522,6 +523,10 @@ static PyObject *
> + conv_content_model(XML_Content * const model,
> + PyObject *(*conv_string)(const XML_Char *))
> + {
> ++ if (_Py_EnterRecursiveCall(" in conv_content_model")) {
> ++ return NULL;
> ++ }
> ++
> + PyObject *result = NULL;
> + PyObject *children = PyTuple_New(model->numchildren);
> + int i;
> +@@ -533,7 +538,7 @@ conv_content_model(XML_Content * const m
> + conv_string);
> + if (child == NULL) {
> + Py_XDECREF(children);
> +- return NULL;
> ++ goto done;
> + }
> + PyTuple_SET_ITEM(children, i, child);
> + }
> +@@ -541,6 +546,8 @@ conv_content_model(XML_Content * const m
> + model->type, model->quant,
> + conv_string,model->name, children);
> + }
> ++done:
> ++ _Py_LeaveRecursiveCall();
> + return result;
> + }
> +
> diff -Nru python3.13-3.13.5/debian/patches/CVE-2026-4519.patch python3.13-3.13.5/debian/patches/CVE-2026-4519.patch
> --- python3.13-3.13.5/debian/patches/CVE-2026-4519.patch 1970-01-01 01:00:00.000000000 +0100
> +++ python3.13-3.13.5/debian/patches/CVE-2026-4519.patch 2026-05-05 23:03:56.000000000 +0200
> @@ -0,0 +1,206 @@
> +From 43fe06b96f6a6cf5cfd5bdab20b8649374956866 Mon Sep 17 00:00:00 2001
> +From: "Miss Islington (bot)"
> + <31488909+miss-islington@users.noreply.github.com>
> +Date: Tue, 24 Mar 2026 00:17:50 +0100
> +Subject: [PATCH] [3.13] gh-143930: Reject leading dashes in webbrowser URLs
> + (GH-146215)
> +
> +From 89bfb8e5ed3c7caa241028f1a4eac5f6275a46a4 Mon Sep 17 00:00:00 2001
> +From: =?UTF-8?q?=C5=81ukasz=20Langa?= <lukasz@langa.pl>
> +Date: Fri, 3 Apr 2026 19:45:02 +0200
> +Subject: [PATCH] [3.13] gh-143930: Tweak the exception message and increase
> + test coverage (GH-146476) (GH-148045)
> +
> +From d6d68494be70bdbda20f89f83801ba52ec37daa4 Mon Sep 17 00:00:00 2001
> +From: "Miss Islington (bot)"
> + <31488909+miss-islington@users.noreply.github.com>
> +Date: Wed, 29 Apr 2026 12:00:10 +0200
> +Subject: [PATCH] [3.13] gh-148169: Fix webbrowser `%action` substitution
> + bypass of dash-prefix check (GH-148170) (#148517)
> +
> +
> +--- python3.13-3.13.5.orig/Lib/test/test_webbrowser.py
> ++++ python3.13-3.13.5/Lib/test/test_webbrowser.py
> +@@ -1,3 +1,4 @@
> ++import io
> + import os
> + import re
> + import shlex
> +@@ -55,6 +56,14 @@ class CommandTestMixin:
> + popen_args.pop(popen_args.index(option))
> + self.assertEqual(popen_args, arguments)
> +
> ++ def test_reject_dash_prefixes(self):
> ++ browser = self.browser_class(name=CMD_NAME)
> ++ with self.assertRaisesRegex(
> ++ ValueError,
> ++ r"^Invalid URL \(leading dash disallowed\): '--key=val http.*'$"
> ++ ):
> ++ browser.open(f"--key=val {URL}")
> ++
> +
> + class GenericBrowserCommandTest(CommandTestMixin, unittest.TestCase):
> +
> +@@ -109,6 +118,15 @@ class ChromeCommandTest(CommandTestMixin
> + arguments=[URL],
> + kw=dict(new=999))
> +
> ++ 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 EdgeCommandTest(CommandTestMixin, unittest.TestCase):
> +
> +@@ -301,6 +319,72 @@ class IOSBrowserTest(unittest.TestCase):
> + self._test('open_new_tab')
> +
> +
> ++class MockPopenPipe:
> ++ def __init__(self, cmd, mode):
> ++ self.cmd = cmd
> ++ self.mode = mode
> ++ self.pipe = io.StringIO()
> ++ self._closed = False
> ++
> ++ def write(self, buf):
> ++ self.pipe.write(buf)
> ++
> ++ def close(self):
> ++ self._closed = True
> ++ return None
> ++
> ++
> ++@unittest.skipUnless(sys.platform == "darwin", "macOS specific test")
> ++@requires_subprocess()
> ++class MacOSXOSAScriptTest(unittest.TestCase):
> ++ def setUp(self):
> ++ # Ensure that 'BROWSER' is not set to 'open' or something else.
> ++ # See: https://github.com/python/cpython/issues/131254.
> ++ env = self.enterContext(os_helper.EnvironmentVarGuard())
> ++ env.unset("BROWSER")
> ++
> ++ support.patch(self, os, "popen", self.mock_popen)
> ++ self.browser = webbrowser.MacOSXOSAScript("default")
> ++
> ++ def mock_popen(self, cmd, mode):
> ++ self.popen_pipe = MockPopenPipe(cmd, mode)
> ++ return self.popen_pipe
> ++
> ++ def test_default(self):
> ++ browser = webbrowser.get()
> ++ assert isinstance(browser, webbrowser.MacOSXOSAScript)
> ++ self.assertEqual(browser.name, "default")
> ++
> ++ def test_default_open(self):
> ++ url = "https://python.org"
> ++ self.browser.open(url)
> ++ self.assertTrue(self.popen_pipe._closed)
> ++ self.assertEqual(self.popen_pipe.cmd, "osascript")
> ++ script = self.popen_pipe.pipe.getvalue()
> ++ self.assertEqual(script.strip(), f'open location "{url}"')
> ++
> ++ def test_url_quote(self):
> ++ self.browser.open('https://python.org/"quote"')
> ++ script = self.popen_pipe.pipe.getvalue()
> ++ self.assertEqual(
> ++ script.strip(), 'open location "https://python.org/%22quote%22"'
> ++ )
> ++
> ++ def test_explicit_browser(self):
> ++ browser = webbrowser.MacOSXOSAScript("safari")
> ++ browser.open("https://python.org")
> ++ script = self.popen_pipe.pipe.getvalue()
> ++ self.assertIn('tell application "safari"', script)
> ++ self.assertIn('open location "https://python.org"', script)
> ++
> ++ def test_reject_dash_prefixes(self):
> ++ with self.assertRaisesRegex(
> ++ ValueError,
> ++ r"^Invalid URL \(leading dash disallowed\): '--key=val http.*'$"
> ++ ):
> ++ self.browser.open(f"--key=val {URL}")
> ++
> ++
> + class BrowserRegistrationTest(unittest.TestCase):
> +
> + def setUp(self):
> +--- python3.13-3.13.5.orig/Lib/webbrowser.py
> ++++ python3.13-3.13.5/Lib/webbrowser.py
> +@@ -164,6 +164,12 @@ class BaseBrowser:
> + def open_new_tab(self, url):
> + return self.open(url, 2)
> +
> ++ @staticmethod
> ++ def _check_url(url):
> ++ """Ensures that the URL is safe to pass to subprocesses as a parameter"""
> ++ if url and url.lstrip().startswith("-"):
> ++ raise ValueError(f"Invalid URL (leading dash disallowed): {url!r}")
> ++
> +
> + class GenericBrowser(BaseBrowser):
> + """Class for all browsers started with a command
> +@@ -181,6 +187,7 @@ class GenericBrowser(BaseBrowser):
> +
> + def open(self, url, new=0, autoraise=True):
> + sys.audit("webbrowser.open", url)
> ++ self._check_url(url)
> + cmdline = [self.name] + [arg.replace("%s", url)
> + for arg in self.args]
> + try:
> +@@ -201,6 +208,7 @@ class BackgroundBrowser(GenericBrowser):
> + cmdline = [self.name] + [arg.replace("%s", url)
> + for arg in self.args]
> + sys.audit("webbrowser.open", url)
> ++ self._check_url(url)
> + try:
> + if sys.platform[:3] == 'win':
> + p = subprocess.Popen(cmdline)
> +@@ -280,7 +288,9 @@ class UnixBrowser(BaseBrowser):
> + raise Error("Bad 'new' parameter to open(); "
> + f"expected 0, 1, or 2, got {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)
> +@@ -358,6 +368,7 @@ class Konqueror(BaseBrowser):
> +
> + def open(self, url, new=0, autoraise=True):
> + sys.audit("webbrowser.open", url)
> ++ self._check_url(url)
> + # XXX Currently I know no way to prevent KFM from opening a new win.
> + if new == 2:
> + action = "newTab"
> +@@ -576,6 +587,7 @@ if sys.platform[:3] == "win":
> + class WindowsDefault(BaseBrowser):
> + def open(self, url, new=0, autoraise=True):
> + sys.audit("webbrowser.open", url)
> ++ self._check_url(url)
> + try:
> + os.startfile(url)
> + except OSError:
> +@@ -596,6 +608,7 @@ if sys.platform == 'darwin':
> +
> + def open(self, url, new=0, autoraise=True):
> + sys.audit("webbrowser.open", url)
> ++ self._check_url(url)
> + url = url.replace('"', '%22')
> + if self.name == 'default':
> + script = f'open location "{url}"' # opens in default browser
> +@@ -627,6 +640,7 @@ if sys.platform == "ios":
> + class IOSBrowser(BaseBrowser):
> + def open(self, url, new=0, autoraise=True):
> + sys.audit("webbrowser.open", url)
> ++ self._check_url(url)
> + # If ctypes isn't available, we can't open a browser
> + if objc is None:
> + return False
> diff -Nru python3.13-3.13.5/debian/patches/CVE-2026-6019.patch python3.13-3.13.5/debian/patches/CVE-2026-6019.patch
> --- python3.13-3.13.5/debian/patches/CVE-2026-6019.patch 1970-01-01 01:00:00.000000000 +0100
> +++ python3.13-3.13.5/debian/patches/CVE-2026-6019.patch 2026-05-05 23:04:58.000000000 +0200
> @@ -0,0 +1,109 @@
> +From 3c59b8b53fc75c7f9578d16fb8201ceb43e8f76c Mon Sep 17 00:00:00 2001
> +From: "Miss Islington (bot)"
> + <31488909+miss-islington@users.noreply.github.com>
> +Date: Thu, 23 Apr 2026 15:05:17 +0200
> +Subject: [PATCH] [3.13] gh-90309: Base64-encode cookie values embedded in JS
> + (GH-148888)
> +
> +--- python3.13-3.13.5.orig/Lib/http/cookies.py
> ++++ python3.13-3.13.5/Lib/http/cookies.py
> +@@ -389,17 +389,21 @@ class Morsel(dict):
> + 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
> +--- python3.13-3.13.5.orig/Lib/test/test_http_cookies.py
> ++++ python3.13-3.13.5/Lib/test/test_http_cookies.py
> +@@ -1,5 +1,5 @@
> + # Simple test suite for http/cookies.py
> +-
> ++import base64
> + import copy
> + import unittest
> + import doctest
> +@@ -153,17 +153,19 @@ class CookieTests(unittest.TestCase, Ext
> +
> + 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>
> + """)
> +@@ -260,17 +262,19 @@ class CookieTests(unittest.TestCase, Ext
> +
> + 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>
> + """)
> +@@ -361,13 +365,16 @@ class MorselTests(unittest.TestCase):
> + 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
> diff -Nru python3.13-3.13.5/debian/patches/CVE-2026-6100.patch python3.13-3.13.5/debian/patches/CVE-2026-6100.patch
> --- python3.13-3.13.5/debian/patches/CVE-2026-6100.patch 1970-01-01 01:00:00.000000000 +0100
> +++ python3.13-3.13.5/debian/patches/CVE-2026-6100.patch 2026-05-05 23:05:45.000000000 +0200
> @@ -0,0 +1,37 @@
> +From c3cf71c3366fe49acb776a639405c0eea6169c20 Mon Sep 17 00:00:00 2001
> +From: "Miss Islington (bot)"
> + <31488909+miss-islington@users.noreply.github.com>
> +Date: Mon, 13 Apr 2026 03:35:24 +0200
> +Subject: [PATCH] [3.13] gh-148395: Fix a possible UAF in
> + `{LZMA,BZ2,_Zlib}Decompressor` (GH-148396) (#148479)
> +
> +--- python3.13-3.13.5.orig/Modules/_bz2module.c
> ++++ python3.13-3.13.5/Modules/_bz2module.c
> +@@ -589,6 +589,7 @@ decompress(BZ2Decompressor *d, char *dat
> + return result;
> +
> + error:
> ++ bzs->next_in = NULL;
> + Py_XDECREF(result);
> + return NULL;
> + }
> +--- python3.13-3.13.5.orig/Modules/_lzmamodule.c
> ++++ python3.13-3.13.5/Modules/_lzmamodule.c
> +@@ -1112,6 +1112,7 @@ decompress(Decompressor *d, uint8_t *dat
> + return result;
> +
> + error:
> ++ lzs->next_in = NULL;
> + Py_XDECREF(result);
> + return NULL;
> + }
> +--- python3.13-3.13.5.orig/Modules/zlibmodule.c
> ++++ python3.13-3.13.5/Modules/zlibmodule.c
> +@@ -1648,6 +1648,7 @@ decompress(ZlibDecompressor *self, uint8
> + return result;
> +
> + error:
> ++ self->zst.next_in = NULL;
> + Py_XDECREF(result);
> + return NULL;
> + }
> diff -Nru python3.13-3.13.5/debian/patches/series python3.13-3.13.5/debian/patches/series
> --- python3.13-3.13.5/debian/patches/series 2026-04-06 14:23:46.000000000 +0200
> +++ python3.13-3.13.5/debian/patches/series 2026-05-05 23:05:32.000000000 +0200
> @@ -42,3 +42,9 @@
> CVE-2026-0865.patch
> CVE-2026-1299.patch
> CVE-2026-2297.patch
> +CVE-2026-3446.patch
> +CVE-2026-4224.patch
> +CVE-2026-3644.patch
> +CVE-2026-4519.patch
> +CVE-2026-6019.patch
> +CVE-2026-6100.patch
Back to linux.debian.bugs.dist | Previous | Next — Next in thread | Find similar
Bug#1135877: trixie-pu: package python3.13/3.13.5-2+deb13u2 Arnaud Rebillout <arnaudr@debian.org> - 2026-05-07 11:30 +0200 Bug#1135877: trixie-pu: package python3.13/3.13.5-2+deb13u2 Moritz Mühlenhoff <jmm@inutil.org> - 2026-05-07 21:00 +0200
csiph-web