Version in base suite: 3.13.5-2 Version in overlay suite: 3.13.5-2+deb13u1 Base version: python3.13_3.13.5-2+deb13u1 Target version: python3.13_3.13.5-2+deb13u2 Base file: /srv/ftp-master.debian.org/ftp/pool/main/p/python3.13/python3.13_3.13.5-2+deb13u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/p/python3.13/python3.13_3.13.5-2+deb13u2.dsc changelog | 11 ++ patches/CVE-2026-3446.patch | 209 ++++++++++++++++++++++++++++++++++++++++++++ patches/CVE-2026-3644.patch | 124 ++++++++++++++++++++++++++ patches/CVE-2026-4224.patch | 71 ++++++++++++++ patches/CVE-2026-4519.patch | 206 +++++++++++++++++++++++++++++++++++++++++++ patches/CVE-2026-6019.patch | 109 ++++++++++++++++++++++ patches/CVE-2026-6100.patch | 37 +++++++ patches/series | 6 + 8 files changed, 773 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpgca1axz_/python3.13_3.13.5-2+deb13u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpgca1axz_/python3.13_3.13.5-2+deb13u2.dsc: no acceptable signature found 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 12:24:14.000000000 +0000 +++ python3.13-3.13.5/debian/changelog 2026-05-05 21:05:52.000000000 +0000 @@ -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 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 00:00:00.000000000 +0000 +++ python3.13-3.13.5/debian/patches/CVE-2026-3446.patch 2026-05-05 12:21:14.000000000 +0000 @@ -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 00:00:00.000000000 +0000 +++ python3.13-3.13.5/debian/patches/CVE-2026-3644.patch 2026-05-05 21:01:49.000000000 +0000 @@ -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 """ + +- """ % (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 00:00:00.000000000 +0000 +++ python3.13-3.13.5/debian/patches/CVE-2026-4224.patch 2026-05-05 12:48:22.000000000 +0000 @@ -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'\n]>\n\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 00:00:00.000000000 +0000 +++ python3.13-3.13.5/debian/patches/CVE-2026-4519.patch 2026-05-05 21:03:56.000000000 +0000 @@ -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?= +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 00:00:00.000000000 +0000 +++ python3.13-3.13.5/debian/patches/CVE-2026-6019.patch 2026-05-05 21:04:58.000000000 +0000 @@ -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 """ + +- """ % (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""" + + """) +- 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""" + + """) +@@ -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""" + + """) +- 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""" + + """) +@@ -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 = """ + +- """ % (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 00:00:00.000000000 +0000 +++ python3.13-3.13.5/debian/patches/CVE-2026-6100.patch 2026-05-05 21:05:45.000000000 +0000 @@ -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 12:23:46.000000000 +0000 +++ python3.13-3.13.5/debian/patches/series 2026-05-05 21:05:32.000000000 +0000 @@ -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