Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python314 for openSUSE:Factory 
checked in at 2026-04-01 19:50:20
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python314 (Old)
 and      /work/SRC/openSUSE:Factory/.python314.new.21863 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python314"

Wed Apr  1 19:50:20 2026 rev:35 rq:1343741 version:3.14.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/python314/python314.changes      2026-03-22 
14:13:07.871724720 +0100
+++ /work/SRC/openSUSE:Factory/.python314.new.21863/python314.changes   
2026-04-01 19:50:21.296431049 +0200
@@ -1,0 +2,35 @@
+Sat Mar 28 18:07:50 UTC 2026 - Matej Cepl <[email protected]>
+
+- Add bsc1260884-llvm21-support.patch which updates the
+  interpreter to use LLVM 21 for building JIT builds
+  (bsc#1260884).
+
+-------------------------------------------------------------------
+Fri Mar 27 17:51:07 UTC 2026 - Matej Cepl <[email protected]>
+
+- Add CVE-2026-4519-webbrowser-open-dashes.patch to reject
+  leading dashes in webbrowser URLs (bsc#1260026, CVE-2026-4519,
+  gh#python/cpython#143930).
+
+-------------------------------------------------------------------
+Wed Mar 25 16:40:18 UTC 2026 - Matej Cepl <[email protected]>
+
+- Add CVE-2025-13462-tarinfo-header-parse.patch which skips
+  TarInfo DIRTYPE normalization during GNU long name handling
+  (bsc#1259611, CVE-2025-13462).
+
+-------------------------------------------------------------------
+Mon Mar 23 22:16:01 UTC 2026 - Matej Cepl <[email protected]>
+
+- Add CVE-2026-4224-expat-unbound-C-recursion.patch avoiding
+  unbound C recursion in conv_content_model in pyexpat.c
+  (bsc#1259735, CVE-2026-4224).
+
+-------------------------------------------------------------------
+Mon Mar 23 17:15:50 UTC 2026 - Matej Cepl <[email protected]>
+
+- Add CVE-2026-3644-cookies-Morsel-update-II.patch to reject
+  control characters in http.cookies.Morsel.update() and
+  http.cookies.BaseCookie.js_output (bsc#1259734, CVE-2026-3644).
+
+-------------------------------------------------------------------

New:
----
  CVE-2025-13462-tarinfo-header-parse.patch
  CVE-2026-3644-cookies-Morsel-update-II.patch
  CVE-2026-4224-expat-unbound-C-recursion.patch
  CVE-2026-4519-webbrowser-open-dashes.patch
  bsc1260884-llvm21-support.patch

----------(New B)----------
  New:
- Add CVE-2025-13462-tarinfo-header-parse.patch which skips
  TarInfo DIRTYPE normalization during GNU long name handling
  New:
- Add CVE-2026-3644-cookies-Morsel-update-II.patch to reject
  control characters in http.cookies.Morsel.update() and
  New:
- Add CVE-2026-4224-expat-unbound-C-recursion.patch avoiding
  unbound C recursion in conv_content_model in pyexpat.c
  New:
- Add CVE-2026-4519-webbrowser-open-dashes.patch to reject
  leading dashes in webbrowser URLs (bsc#1260026, CVE-2026-4519,
  New:
- Add bsc1260884-llvm21-support.patch which updates the
  interpreter to use LLVM 21 for building JIT builds
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python314.spec ++++++
--- /var/tmp/diff_new_pack.cy1LvK/_old  2026-04-01 19:50:24.188551521 +0200
+++ /var/tmp/diff_new_pack.cy1LvK/_new  2026-04-01 19:50:24.192551687 +0200
@@ -241,6 +241,21 @@
 # PATCH-FIX-UPSTREAM CVE-2026-2297-SourcelessFileLoader-io_open_code.patch 
bsc#1259240 [email protected]
 # Ensure SourcelessFileLoader uses io.open_code
 Patch52:        CVE-2026-2297-SourcelessFileLoader-io_open_code.patch
+# PATCH-FIX-UPSTREAM CVE-2026-3644-cookies-Morsel-update-II.patch bsc#1259734 
[email protected]
+# Reject control characters in http.cookies.Morsel.update() and 
http.cookies.BaseCookie.js_output
+Patch53:        CVE-2026-3644-cookies-Morsel-update-II.patch
+# PATCH-FIX-UPSTREAM CVE-2026-4224-expat-unbound-C-recursion.patch bsc#1259735 
[email protected]
+# Avoid unbound C recursion in conv_content_model
+Patch54:        CVE-2026-4224-expat-unbound-C-recursion.patch
+# PATCH-FIX-UPSTREAM CVE-2025-13462-tarinfo-header-parse.patch bsc#1259611 
[email protected]
+# Skip TarInfo DIRTYPE normalization during GNU long name handling
+Patch55:        CVE-2025-13462-tarinfo-header-parse.patch
+# PATCH-FIX-UPSTREAM CVE-2026-4519-webbrowser-open-dashes.patch bsc#1260026 
[email protected]
+# reject leading dashes in webbrowser URLs
+Patch56:        CVE-2026-4519-webbrowser-open-dashes.patch
+# PATCH-FIX-OPENSUSE bsc1260884-llvm21-support.patch bsc#1260884 [email protected]
+# update JIT builds to use LLVM 21
+Patch57:        bsc1260884-llvm21-support.patch
 #### Python 3.14 END OF PATCHES
 BuildRequires:  autoconf-archive
 BuildRequires:  automake
@@ -286,7 +301,7 @@
 
 %if %{with experimental_jit}
 # needed for experimental_jit
-BuildRequires:  clang19 llvm19
+BuildRequires:  clang21 llvm21
 BuildRequires:  llvm
 %endif
 

++++++ CVE-2025-13462-tarinfo-header-parse.patch ++++++
>From 2551db3b31e675daf9671ad9c3f81585554ef382 Mon Sep 17 00:00:00 2001
From: Seth Michael Larson <[email protected]>
Date: Wed, 11 Mar 2026 08:47:55 -0500
Subject: [PATCH] gh-141707: Skip TarInfo DIRTYPE normalization during GNU long
 name handling (cherry picked from commit
 42d754e34c06e57ad6b8e7f92f32af679912d8ab)

Co-authored-by: Seth Michael Larson <[email protected]>
Co-authored-by: Eashwar Ranganathan <[email protected]>
---
 Lib/tarfile.py                                                          |   29 
++++++++--
 Lib/test/test_tarfile.py                                                |   19 
++++++
 Misc/ACKS                                                               |    1 
 Misc/NEWS.d/next/Library/2025-11-18-06-35-53.gh-issue-141707.DBmQIy.rst |    2 
 4 files changed, 47 insertions(+), 4 deletions(-)
 create mode 100644 
Misc/NEWS.d/next/Library/2025-11-18-06-35-53.gh-issue-141707.DBmQIy.rst

Index: Python-3.14.3/Lib/tarfile.py
===================================================================
--- Python-3.14.3.orig/Lib/tarfile.py   2026-03-26 00:01:07.867565744 +0100
+++ Python-3.14.3/Lib/tarfile.py        2026-03-26 00:01:18.578639036 +0100
@@ -1278,6 +1278,20 @@
     @classmethod
     def frombuf(cls, buf, encoding, errors):
         """Construct a TarInfo object from a 512 byte bytes object.
+
+        To support the old v7 tar format AREGTYPE headers are
+        transformed to DIRTYPE headers if their name ends in '/'.
+        """
+        return cls._frombuf(buf, encoding, errors)
+
+    @classmethod
+    def _frombuf(cls, buf, encoding, errors, *, dircheck=True):
+        """Construct a TarInfo object from a 512 byte bytes object.
+
+        If ``dircheck`` is set to ``True`` then ``AREGTYPE`` headers will
+        be normalized to ``DIRTYPE`` if the name ends in a trailing slash.
+        ``dircheck`` must be set to ``False`` if this function is called
+        on a follow-up header such as ``GNUTYPE_LONGNAME``.
         """
         if len(buf) == 0:
             raise EmptyHeaderError("empty header")
@@ -1308,7 +1322,7 @@
 
         # Old V7 tar format represents a directory as a regular
         # file with a trailing slash.
-        if obj.type == AREGTYPE and obj.name.endswith("/"):
+        if dircheck and obj.type == AREGTYPE and obj.name.endswith("/"):
             obj.type = DIRTYPE
 
         # The old GNU sparse format occupies some of the unused
@@ -1343,8 +1357,15 @@
         """Return the next TarInfo object from TarFile object
            tarfile.
         """
+        return cls._fromtarfile(tarfile)
+
+    @classmethod
+    def _fromtarfile(cls, tarfile, *, dircheck=True):
+        """
+        See dircheck documentation in _frombuf().
+        """
         buf = tarfile.fileobj.read(BLOCKSIZE)
-        obj = cls.frombuf(buf, tarfile.encoding, tarfile.errors)
+        obj = cls._frombuf(buf, tarfile.encoding, tarfile.errors, 
dircheck=dircheck)
         obj.offset = tarfile.fileobj.tell() - BLOCKSIZE
         return obj._proc_member(tarfile)
 
@@ -1402,7 +1423,7 @@
 
         # Fetch the next header and process it.
         try:
-            next = self.fromtarfile(tarfile)
+            next = self._fromtarfile(tarfile, dircheck=False)
         except HeaderError as e:
             raise SubsequentHeaderError(str(e)) from None
 
@@ -1537,7 +1558,7 @@
 
         # Fetch the next header.
         try:
-            next = self.fromtarfile(tarfile)
+            next = self._fromtarfile(tarfile, dircheck=False)
         except HeaderError as e:
             raise SubsequentHeaderError(str(e)) from None
 
Index: Python-3.14.3/Lib/test/test_tarfile.py
===================================================================
--- Python-3.14.3.orig/Lib/test/test_tarfile.py 2026-03-26 00:01:11.137683961 
+0100
+++ Python-3.14.3/Lib/test/test_tarfile.py      2026-03-26 00:01:18.580535009 
+0100
@@ -1234,6 +1234,25 @@
                 self.assertIsNotNone(tar.getmember(longdir))
                 self.assertIsNotNone(tar.getmember(longdir.removesuffix('/')))
 
+    def test_longname_file_not_directory(self):
+        # Test reading a longname file and ensure it is not handled as a 
directory
+        # Issue #141707
+        buf = io.BytesIO()
+        with tarfile.open(mode='w', fileobj=buf, format=self.format) as tar:
+            ti = tarfile.TarInfo()
+            ti.type = tarfile.AREGTYPE
+            ti.name = ('a' * 99) + '/' + ('b' * 3)
+            tar.addfile(ti)
+
+            expected = {t.name: t.type for t in tar.getmembers()}
+
+        buf.seek(0)
+        with tarfile.open(mode='r', fileobj=buf) as tar:
+            actual = {t.name: t.type for t in tar.getmembers()}
+
+        self.assertEqual(expected, actual)
+
+
 class GNUReadTest(LongnameTest, ReadTest, unittest.TestCase):
 
     subdir = "gnu"
Index: Python-3.14.3/Misc/ACKS
===================================================================
--- Python-3.14.3.orig/Misc/ACKS        2026-02-03 16:32:20.000000000 +0100
+++ Python-3.14.3/Misc/ACKS     2026-03-26 00:01:18.581228076 +0100
@@ -1534,6 +1534,7 @@
 Jeff Ramnani
 Grant Ramsay
 Bayard Randel
+Eashwar Ranganathan
 Varpu Rantala
 Brodie Rao
 Rémi Rampin
Index: 
Python-3.14.3/Misc/NEWS.d/next/Library/2025-11-18-06-35-53.gh-issue-141707.DBmQIy.rst
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ 
Python-3.14.3/Misc/NEWS.d/next/Library/2025-11-18-06-35-53.gh-issue-141707.DBmQIy.rst
       2026-03-26 00:01:18.581551045 +0100
@@ -0,0 +1,2 @@
+Don't change :class:`tarfile.TarInfo` type from ``AREGTYPE`` to ``DIRTYPE`` 
when parsing
+GNU long name or link headers.

++++++ CVE-2026-3644-cookies-Morsel-update-II.patch ++++++
>From cf297a55b7ade3b3f90b6658ea3dc507fc795afe Mon Sep 17 00:00:00 2001
From: Stan Ulbrych <[email protected]>
Date: Mon, 16 Mar 2026 13:43:43 +0000
Subject: [PATCH] gh-145599, CVE 2026-3644: Reject control characters in
 `http.cookies.Morsel.update()` (GH-145600)

Reject control characters in `http.cookies.Morsel.update()` and 
`http.cookies.BaseCookie.js_output`.
(cherry picked from commit 57e88c1cf95e1481b94ae57abe1010469d47a6b4)

Co-authored-by: Stan Ulbrych <[email protected]>
Co-authored-by: Victor Stinner <[email protected]>
Co-authored-by: Victor Stinner <[email protected]>
---
 Lib/http/cookies.py                                                      |   
24 +++++-
 Lib/test/test_http_cookies.py                                            |   
38 ++++++++++
 Misc/NEWS.d/next/Security/2026-03-06-17-03-38.gh-issue-145599.kchwZV.rst |    
4 +
 3 files changed, 62 insertions(+), 4 deletions(-)
 create mode 100644 
Misc/NEWS.d/next/Security/2026-03-06-17-03-38.gh-issue-145599.kchwZV.rst

Index: Python-3.14.3/Lib/http/cookies.py
===================================================================
--- Python-3.14.3.orig/Lib/http/cookies.py      2026-03-23 18:22:56.087308142 
+0100
+++ Python-3.14.3/Lib/http/cookies.py   2026-03-23 18:23:02.379220922 +0100
@@ -337,9 +337,16 @@
             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
 
@@ -365,9 +372,15 @@
         }
 
     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))
@@ -379,13 +392,16 @@
 
     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
Index: Python-3.14.3/Lib/test/test_http_cookies.py
===================================================================
--- Python-3.14.3.orig/Lib/test/test_http_cookies.py    2026-03-23 
18:22:57.946687778 +0100
+++ Python-3.14.3/Lib/test/test_http_cookies.py 2026-03-23 18:23:02.379416641 
+0100
@@ -581,6 +581,14 @@
             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)
@@ -595,6 +603,18 @@
             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.
@@ -615,6 +635,24 @@
             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))
Index: 
Python-3.14.3/Misc/NEWS.d/next/Security/2026-03-06-17-03-38.gh-issue-145599.kchwZV.rst
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ 
Python-3.14.3/Misc/NEWS.d/next/Security/2026-03-06-17-03-38.gh-issue-145599.kchwZV.rst
      2026-03-23 18:23:02.379601880 +0100
@@ -0,0 +1,4 @@
+Reject control characters in :class:`http.cookies.Morsel`
+:meth:`~http.cookies.Morsel.update` and
+:meth:`~http.cookies.BaseCookie.js_output`.
+This addresses :cve:`2026-3644`.

++++++ CVE-2026-4224-expat-unbound-C-recursion.patch ++++++
>From aa98922b108265ae67e18cea85174f1fd2af51aa Mon Sep 17 00:00:00 2001
From: Stan Ulbrych <[email protected]>
Date: Sun, 15 Mar 2026 21:46:06 +0000
Subject: [PATCH] gh-145986: Avoid unbound C recursion in `conv_content_model`
 in `pyexpat.c` (CVE 2026-4224) (GH-145987)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fix C stack overflow (CVE-2026-4224) when an Expat parser
with a registered `ElementDeclHandler` parses inline DTD
containing deeply nested content model.

---------
(cherry picked from commit eb0e8be3a7e11b87d198a2c3af1ed0eccf532768)

Co-authored-by: Stan Ulbrych <[email protected]>
Co-authored-by: Bénédikt Tran <[email protected]>
---
 Lib/test/test_pyexpat.py                                                 |   
19 ++++++++++
 Misc/NEWS.d/next/Security/2026-03-14-17-31-39.gh-issue-145986.ifSSr8.rst |    
4 ++
 Modules/pyexpat.c                                                        |    
9 ++++
 3 files changed, 31 insertions(+), 1 deletion(-)
 create mode 100644 
Misc/NEWS.d/next/Security/2026-03-14-17-31-39.gh-issue-145986.ifSSr8.rst

Index: Python-3.14.3/Lib/test/test_pyexpat.py
===================================================================
--- Python-3.14.3.orig/Lib/test/test_pyexpat.py 2026-03-24 09:27:25.034071315 
+0100
+++ Python-3.14.3/Lib/test/test_pyexpat.py      2026-03-24 09:27:25.175636507 
+0100
@@ -689,6 +689,25 @@
         parser.ElementDeclHandler = lambda _1, _2: None
         self.assertRaises(TypeError, parser.Parse, data, True)
 
+    @support.skip_if_unlimited_stack_size
+    @support.skip_emscripten_stack_overflow()
+    @support.skip_wasi_stack_overflow()
+    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"
Index: 
Python-3.14.3/Misc/NEWS.d/next/Security/2026-03-14-17-31-39.gh-issue-145986.ifSSr8.rst
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ 
Python-3.14.3/Misc/NEWS.d/next/Security/2026-03-14-17-31-39.gh-issue-145986.ifSSr8.rst
      2026-03-24 09:27:25.175848336 +0100
@@ -0,0 +1,4 @@
+:mod:`xml.parsers.expat`: Fixed a crash caused by unbounded C recursion when
+converting deeply nested XML content models with
+:meth:`~xml.parsers.expat.xmlparser.ElementDeclHandler`.
+This addresses :cve:`2026-4224`.
Index: Python-3.14.3/Modules/pyexpat.c
===================================================================
--- Python-3.14.3.orig/Modules/pyexpat.c        2026-02-03 16:32:20.000000000 
+0100
+++ Python-3.14.3/Modules/pyexpat.c     2026-03-24 09:27:25.176330576 +0100
@@ -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()
@@ -603,6 +604,10 @@
 conv_content_model(XML_Content * const model,
                    PyObject *(*conv_string)(void *))
 {
+    if (_Py_EnterRecursiveCall(" in conv_content_model")) {
+        return NULL;
+    }
+
     PyObject *result = NULL;
     PyObject *children = PyTuple_New(model->numchildren);
     int i;
@@ -614,7 +619,7 @@
                                                  conv_string);
             if (child == NULL) {
                 Py_XDECREF(children);
-                return NULL;
+                goto done;
             }
             PyTuple_SET_ITEM(children, i, child);
         }
@@ -622,6 +627,8 @@
                                model->type, model->quant,
                                conv_string, model->name, children);
     }
+done:
+    _Py_LeaveRecursiveCall();
     return result;
 }
 

++++++ CVE-2026-4519-webbrowser-open-dashes.patch ++++++
>From 23cdf332ffefa7f4577df96d011e014fc81dd220 Mon Sep 17 00:00:00 2001
From: Seth Michael Larson <[email protected]>
Date: Fri, 20 Mar 2026 09:47:13 -0500
Subject: [PATCH] gh-143930: Reject leading dashes in webbrowser URLs (cherry
 picked from commit 82a24a4442312bdcfc4c799885e8b3e00990f02b)

Co-authored-by: Seth Michael Larson <[email protected]>
---
 Lib/test/test_webbrowser.py                                              |    
5 +++
 Lib/webbrowser.py                                                        |   
13 ++++++++++
 Misc/NEWS.d/next/Security/2026-01-16-12-04-49.gh-issue-143930.zYC5x3.rst |    
1 
 3 files changed, 19 insertions(+)
 create mode 100644 
Misc/NEWS.d/next/Security/2026-01-16-12-04-49.gh-issue-143930.zYC5x3.rst

Index: Python-3.14.3/Lib/test/test_webbrowser.py
===================================================================
--- Python-3.14.3.orig/Lib/test/test_webbrowser.py      2026-03-27 
20:06:01.180854379 +0100
+++ Python-3.14.3/Lib/test/test_webbrowser.py   2026-03-27 20:06:03.883016424 
+0100
@@ -67,6 +67,11 @@
                    options=[],
                    arguments=[URL])
 
+    def test_reject_dash_prefixes(self):
+        browser = self.browser_class(name=CMD_NAME)
+        with self.assertRaises(ValueError):
+            browser.open(f"--key=val {URL}")
+
 
 class BackgroundBrowserCommandTest(CommandTestMixin, unittest.TestCase):
 
Index: Python-3.14.3/Lib/webbrowser.py
===================================================================
--- Python-3.14.3.orig/Lib/webbrowser.py        2026-03-27 20:06:01.565209291 
+0100
+++ Python-3.14.3/Lib/webbrowser.py     2026-03-27 20:06:03.883196593 +0100
@@ -163,6 +163,12 @@
     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: {url}")
+
 
 class GenericBrowser(BaseBrowser):
     """Class for all browsers started with a command
@@ -180,6 +186,7 @@
 
     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:
@@ -200,6 +207,7 @@
         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)
@@ -266,6 +274,7 @@
 
     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:
@@ -357,6 +366,7 @@
 
     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"
@@ -588,6 +598,7 @@
     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:
@@ -608,6 +619,7 @@
 
         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':
                 proto, _sep, _rest = url.partition(":")
@@ -664,6 +676,7 @@
     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
Index: 
Python-3.14.3/Misc/NEWS.d/next/Security/2026-01-16-12-04-49.gh-issue-143930.zYC5x3.rst
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ 
Python-3.14.3/Misc/NEWS.d/next/Security/2026-01-16-12-04-49.gh-issue-143930.zYC5x3.rst
      2026-03-27 20:06:03.883428230 +0100
@@ -0,0 +1 @@
+Reject leading dashes in URLs passed to :func:`webbrowser.open`

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.cy1LvK/_old  2026-04-01 19:50:24.756575183 +0200
+++ /var/tmp/diff_new_pack.cy1LvK/_new  2026-04-01 19:50:24.768575682 +0200
@@ -1,6 +1,6 @@
-mtime: 1773426578
-commit: 73dfe9303e9ff663b2dfe791fe4ba2d75cc0b90e43a32c3a3f81cb031a2c58c3
+mtime: 1774721353
+commit: 2f7ec348f127519f8b603c3d5477bd834349369c8a216486a7264a6c493227ed
 url: https://src.opensuse.org/python-interpreters/python314.git
-revision: 73dfe9303e9ff663b2dfe791fe4ba2d75cc0b90e43a32c3a3f81cb031a2c58c3
+revision: 2f7ec348f127519f8b603c3d5477bd834349369c8a216486a7264a6c493227ed
 projectscmsync: https://src.opensuse.org/python-interpreters/_ObsPrj
 

++++++ bsc1260884-llvm21-support.patch ++++++
>From adb272130fdad3e8d86cfff2630eeb57b39a2c4e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20Cepl?= <[email protected]>
Date: Sat, 28 Mar 2026 19:02:56 +0100
Subject: [PATCH] GH-136895: Update JIT builds to use LLVM 21

Upgrade the JIT toolchain from LLVM 19 to LLVM 21 (21.1.4), covering
CI workflows, build-time scripts, documentation, and the runtime code
generator.

LLVM version bump:
- Update LLVM_VERSION in jit.yml and tail-call.yml CI workflows to 21.
- Bump _LLVM_VERSION and _EXTERNALS_LLVM_TAG in Tools/jit/_llvm.py.
- Update all install instructions in Tools/jit/README.md (Ubuntu/Debian,
  Fedora, macOS Homebrew, Windows chocolatey, Dev Containers).

JIT runtime changes (Python/jit.c):
- Add x86_64 trampoline support for out-of-range GOT entries. LLVM 20+
  can produce GOT references exceeding the +/-2GB PC-relative range on
  macOS x86_64 debug builds; trampolines use a 14-byte jmp *(%rip) stub
  with an embedded 64-bit absolute address, padded to 16 bytes.
- Refactor trampoline slot lookup into a shared get_trampoline_slot()
  helper, used by both AArch64 and x86_64 trampoline patchers.
- Enable x86_64 trampoline infrastructure on all x86_64 platforms
  (not just macOS) as a defensive measure against future LLVM changes.
- Introduce DATA_ALIGN macro and add alignment padding between the
  code+trampoline region and the data section for correct data alignment.

JIT build-time changes (Tools/jit/):
- Handle X86_64_RELOC_BRANCH Mach-O relocations in _stencils.py,
  routing external symbol references through patch_x86_64_trampoline.
- Move -fno-plt from global compiler flags to Linux-only targets
  (aarch64-linux-gnu and x86_64-linux-gnu) in _targets.py. Remove the
  now-unnecessary -fplt counterweight from aarch64-pc-windows-msvc.
- Improve LLVM tool discovery on Windows in _llvm.py by trying both
  bare tool names and .exe-suffixed variants at every search stage.

This commit excludes Windows-specific PCbuild changes (get_external.py,
get_externals.bat) which require separate handling for the LLVM release
tarball download infrastructure.
---
 
Misc/NEWS.d/next/Core_and_Builtins/2025-10-19-10-32-28.gh-issue-136895.HfsEh0.rst
 |    1 
 Python/jit.c                                                                   
   |   90 ++++++++--
 Tools/jit/README.md                                                            
   |   22 +-
 Tools/jit/_llvm.py                                                             
   |   37 ++--
 Tools/jit/_stencils.py                                                         
   |   17 +
 Tools/jit/_targets.py                                                          
   |    9 -
 6 files changed, 129 insertions(+), 47 deletions(-)
 create mode 100644 
Misc/NEWS.d/next/Core_and_Builtins/2025-10-19-10-32-28.gh-issue-136895.HfsEh0.rst

Index: 
Python-3.14.3/Misc/NEWS.d/next/Core_and_Builtins/2025-10-19-10-32-28.gh-issue-136895.HfsEh0.rst
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ 
Python-3.14.3/Misc/NEWS.d/next/Core_and_Builtins/2025-10-19-10-32-28.gh-issue-136895.HfsEh0.rst
     2026-03-28 19:07:08.905250841 +0100
@@ -0,0 +1 @@
+Update JIT compilation to use LLVM 21 at build time.
Index: Python-3.14.3/Python/jit.c
===================================================================
--- Python-3.14.3.orig/Python/jit.c     2026-02-03 16:32:20.000000000 +0100
+++ Python-3.14.3/Python/jit.c  2026-03-28 19:07:08.905586532 +0100
@@ -419,15 +419,43 @@
 }
 
 void patch_aarch64_trampoline(unsigned char *location, int ordinal, jit_state 
*state);
+void patch_x86_64_trampoline(unsigned char *location, int ordinal, jit_state 
*state);
 
 #include "jit_stencils.h"
 
 #if defined(__aarch64__) || defined(_M_ARM64)
     #define TRAMPOLINE_SIZE 16
+    #define DATA_ALIGN 8
+#elif defined(__x86_64__) || defined(_M_X64)
+    // x86_64 trampolines: 14 bytes (jmp *(%rip) + 8-byte addr) + 2 bytes 
padding.
+    // Currently used on macOS where LLVM 21 GOT entries may exceed ±2GB
+    // PC-relative range, but enabled on all x86_64 platforms defensively.
+    #define TRAMPOLINE_SIZE 16
+    #define DATA_ALIGN 8
 #else
     #define TRAMPOLINE_SIZE 0
+    #define DATA_ALIGN 1
 #endif
 
+// Get the trampoline memory location for a given symbol ordinal.
+static unsigned char *
+get_trampoline_slot(int ordinal, jit_state *state)
+{
+    const uint32_t symbol_mask = 1 << (ordinal % 32);
+    const uint32_t trampoline_mask = state->trampolines.mask[ordinal / 32];
+    assert(symbol_mask & trampoline_mask);
+
+    // Count the number of set bits in the trampoline mask lower than ordinal.
+    int index = _Py_popcount32(trampoline_mask & (symbol_mask - 1));
+    for (int i = 0; i < ordinal / 32; i++) {
+        index += _Py_popcount32(state->trampolines.mask[i]);
+    }
+
+    unsigned char *trampoline = state->trampolines.mem + index * 
TRAMPOLINE_SIZE;
+    assert((size_t)(index + 1) * TRAMPOLINE_SIZE <= state->trampolines.size);
+    return trampoline;
+}
+
 // Generate and patch AArch64 trampolines. The symbols to jump to are stored
 // in the jit_stencils.h in the symbols_map.
 void
@@ -444,20 +472,8 @@
         return;
     }
 
-    // Masking is done modulo 32 as the mask is stored as an array of uint32_t
-    const uint32_t symbol_mask = 1 << (ordinal % 32);
-    const uint32_t trampoline_mask = state->trampolines.mask[ordinal / 32];
-    assert(symbol_mask & trampoline_mask);
-
-    // Count the number of set bits in the trampoline mask lower than ordinal,
-    // this gives the index into the array of trampolines.
-    int index = _Py_popcount32(trampoline_mask & (symbol_mask - 1));
-    for (int i = 0; i < ordinal / 32; i++) {
-        index += _Py_popcount32(state->trampolines.mask[i]);
-    }
-
-    uint32_t *p = (uint32_t*)(state->trampolines.mem + index * 
TRAMPOLINE_SIZE);
-    assert((size_t)(index + 1) * TRAMPOLINE_SIZE <= state->trampolines.size);
+    // Out of range - need a trampoline
+    uint32_t *p = (uint32_t *)get_trampoline_slot(ordinal, state);
 
 
     /* Generate the trampoline
@@ -474,6 +490,37 @@
     patch_aarch64_26r(location, (uintptr_t)p);
 }
 
+// Generate and patch x86_64 trampolines.
+void
+patch_x86_64_trampoline(unsigned char *location, int ordinal, jit_state *state)
+{
+    uint64_t value = (uintptr_t)symbols_map[ordinal];
+    int64_t range = (int64_t)value - 4 - (int64_t)location;
+
+    // If we are in range of 32 signed bits, we can patch directly
+    if (range >= -(1LL << 31) && range < (1LL << 31)) {
+        patch_32r(location, value - 4);
+        return;
+    }
+
+    // Out of range - need a trampoline
+    unsigned char *trampoline = get_trampoline_slot(ordinal, state);
+
+    /* Generate the trampoline (14 bytes, padded to 16):
+       0: ff 25 00 00 00 00    jmp *(%rip)
+       6: XX XX XX XX XX XX XX XX   (64-bit target address)
+
+       Reference: https://wiki.osdev.org/X86-64_Instruction_Encoding#FF (JMP 
r/m64)
+    */
+    trampoline[0] = 0xFF;
+    trampoline[1] = 0x25;
+    memset(trampoline + 2, 0, 4);
+    memcpy(trampoline + 6, &value, 8);
+
+    // Patch the call site to call the trampoline instead
+    patch_32r(location, (uintptr_t)trampoline - 4);
+}
+
 static void
 combine_symbol_mask(const symbol_mask src, symbol_mask dest)
 {
@@ -515,8 +562,13 @@
     // Round up to the nearest page:
     size_t page_size = get_page_size();
     assert((page_size & (page_size - 1)) == 0);
-    size_t padding = page_size - ((code_size + state.trampolines.size + 
data_size) & (page_size - 1));
-    size_t total_size = code_size + state.trampolines.size + data_size  + 
padding;
+    size_t code_padding =
+        DATA_ALIGN - ((code_size + state.trampolines.size) & (DATA_ALIGN - 1));
+    size_t padding = page_size -
+        ((code_size + state.trampolines.size + code_padding + data_size) &
+         (page_size - 1));
+    size_t total_size =
+        code_size + state.trampolines.size + code_padding + data_size + 
padding;
     unsigned char *memory = jit_alloc(total_size);
     if (memory == NULL) {
         return -1;
@@ -535,7 +587,7 @@
     // Loop again to emit the code:
     unsigned char *code = memory;
     state.trampolines.mem = memory + code_size;
-    unsigned char *data = memory + code_size + state.trampolines.size;
+    unsigned char *data = memory + code_size + state.trampolines.size + 
code_padding;
     // Compile the shim, which handles converting between the native
     // calling convention and the calling convention used by jitted code
     // (which may be different for efficiency reasons).
@@ -557,7 +609,9 @@
     code += group->code_size;
     data += group->data_size;
     assert(code == memory + code_size);
-    assert(data == memory + code_size + state.trampolines.size + data_size);
+    assert(
+        data ==
+        memory + code_size + state.trampolines.size + code_padding + 
data_size);
     if (mark_executable(memory, total_size)) {
         jit_free(memory, total_size);
         return -1;
Index: Python-3.14.3/Tools/jit/README.md
===================================================================
--- Python-3.14.3.orig/Tools/jit/README.md      2026-02-03 16:32:20.000000000 
+0100
+++ Python-3.14.3/Tools/jit/README.md   2026-03-28 19:07:08.905766140 +0100
@@ -9,32 +9,32 @@
 
 The JIT compiler does not require end users to install any third-party 
dependencies, but part of it must be *built* using LLVM[^why-llvm]. You are 
*not* required to build the rest of CPython using LLVM, or even the same 
version of LLVM (in fact, this is uncommon).
 
-LLVM version 19 is required. Both `clang` and `llvm-readobj` need to be 
installed and discoverable (version suffixes, like `clang-19`, are okay). It's 
highly recommended that you also have `llvm-objdump` available, since this 
allows the build script to dump human-readable assembly for the generated code.
+LLVM version 21 is the officially supported version. You can modify if needed 
using the `LLVM_VERSION` env var during configure. Both `clang` and 
`llvm-readobj` need to be installed and discoverable (version suffixes, like 
`clang-21`, are okay). It's highly recommended that you also have 
`llvm-objdump` available, since this allows the build script to dump 
human-readable assembly for the generated code.
 
 It's easy to install all of the required tools:
 
 ### Linux
 
-Install LLVM 19 on Ubuntu/Debian:
+Install LLVM 21 on Ubuntu/Debian:
 
 ```sh
 wget https://apt.llvm.org/llvm.sh
 chmod +x llvm.sh
-sudo ./llvm.sh 19
+sudo ./llvm.sh 21
 ```
 
-Install LLVM 19 on Fedora Linux 40 or newer:
+Install LLVM 21 on Fedora Linux 40 or newer:
 
 ```sh
-sudo dnf install 'clang(major) = 19' 'llvm(major) = 19'
+sudo dnf install 'clang(major) = 21' 'llvm(major) = 21'
 ```
 
 ### macOS
 
-Install LLVM 19 with [Homebrew](https://brew.sh):
+Install LLVM 21 with [Homebrew](https://brew.sh):
 
 ```sh
-brew install llvm@19
+brew install llvm@21
 ```
 
 Homebrew won't add any of the tools to your `$PATH`. That's okay; the build 
script knows how to find them.
@@ -43,14 +43,18 @@
 
 LLVM is downloaded automatically (along with other external binary 
dependencies) by `PCbuild\build.bat`.
 
-Otherwise, you can install LLVM 19 [by searching for it on LLVM's GitHub 
releases page](https://github.com/llvm/llvm-project/releases?q=19), clicking on 
"Assets", downloading the appropriate Windows installer for your platform 
(likely the file ending with `-win64.exe`), and running it. **When installing, 
be sure to select the option labeled "Add LLVM to the system PATH".**
+Otherwise, you can install LLVM 21 [by searching for it on LLVM's GitHub 
releases page](https://github.com/llvm/llvm-project/releases?q=21), clicking on 
"Assets", downloading the appropriate Windows installer for your platform 
(likely the file ending with `-win64.exe`), and running it. **When installing, 
be sure to select the option labeled "Add LLVM to the system PATH".**
 
 Alternatively, you can use [chocolatey](https://chocolatey.org):
 
 ```sh
-choco install llvm --version=19.1.0
+choco install llvm --version=21.1.8
 ```
 
+### Dev Containers
+
+If you are working on CPython in a [Codespaces 
instance](https://devguide.python.org/getting-started/setup-building/#using-codespaces),
 there's no
+need to install LLVM as the Fedora 42 base image includes LLVM 21 out of the 
box.
 
 ## Building
 
Index: Python-3.14.3/Tools/jit/_llvm.py
===================================================================
--- Python-3.14.3.orig/Tools/jit/_llvm.py       2026-03-28 19:07:01.506684972 
+0100
+++ Python-3.14.3/Tools/jit/_llvm.py    2026-03-28 19:07:08.905859205 +0100
@@ -10,9 +10,9 @@
 
 import _targets
 
-_LLVM_VERSION = 19
+_LLVM_VERSION = 21
 _LLVM_VERSION_PATTERN = 
re.compile(rf"version\s+{_LLVM_VERSION}\.\d+\.\d+\S*\s+")
-_EXTERNALS_LLVM_TAG = "llvm-19.1.7.0"
+_EXTERNALS_LLVM_TAG = "llvm-21.1.4.0"
 
 _P = typing.ParamSpec("_P")
 _R = typing.TypeVar("_R")
@@ -38,6 +38,13 @@
 _CORES = asyncio.BoundedSemaphore(os.cpu_count() or 1)
 
 
+def _candidate_names(tool: str) -> list[str]:
+    candidates = [tool]
+    if os.name == "nt":
+        candidates.append(f"{tool}.exe")
+    return candidates
+
+
 async def _run(tool: str, args: typing.Iterable[str], echo: bool = False) -> 
str | None:
     command = [tool, *args]
     async with _CORES:
@@ -70,24 +77,26 @@
 @_async_cache
 async def _find_tool(tool: str, *, echo: bool = False) -> str | None:
     # Unversioned executables:
-    path = tool
-    if await _check_tool_version(path, echo=echo):
-        return path
+    for path in _candidate_names(tool):
+        if await _check_tool_version(path, echo=echo):
+            return path
     # Versioned executables:
-    path = f"{tool}-{_LLVM_VERSION}"
-    if await _check_tool_version(path, echo=echo):
-        return path
+    for path in _candidate_names(f"{tool}-{_LLVM_VERSION}"):
+        if await _check_tool_version(path, echo=echo):
+            return path
     # PCbuild externals:
     externals = os.environ.get("EXTERNALS_DIR", _targets.EXTERNALS)
-    path = os.path.join(externals, _EXTERNALS_LLVM_TAG, "bin", tool)
-    if await _check_tool_version(path, echo=echo):
-        return path
+    for name in _candidate_names(tool):
+        path = os.path.join(externals, _EXTERNALS_LLVM_TAG, "bin", name)
+        if await _check_tool_version(path, echo=echo):
+            return path
     # Homebrew-installed executables:
     prefix = await _get_brew_llvm_prefix(echo=echo)
     if prefix is not None:
-        path = os.path.join(prefix, "bin", tool)
-        if await _check_tool_version(path, echo=echo):
-            return path
+        for name in _candidate_names(tool):
+            path = os.path.join(prefix, "bin", name)
+            if await _check_tool_version(path, echo=echo):
+                return path
     # Nothing found:
     return None
 
Index: Python-3.14.3/Tools/jit/_stencils.py
===================================================================
--- Python-3.14.3.orig/Tools/jit/_stencils.py   2026-03-28 19:07:01.511197803 
+0100
+++ Python-3.14.3/Tools/jit/_stencils.py        2026-03-28 19:07:08.905962259 
+0100
@@ -302,6 +302,23 @@
                 self._trampolines.add(ordinal)
                 hole.addend = ordinal
                 hole.symbol = None
+            # x86_64 Darwin trampolines for external symbols
+            elif (
+                hole.kind == "X86_64_RELOC_BRANCH"
+                and hole.value is HoleValue.ZERO
+                and hole.symbol not in self.symbols
+            ):
+                hole.func = "patch_x86_64_trampoline"
+                hole.need_state = True
+                assert hole.symbol is not None
+                if hole.symbol in known_symbols:
+                    ordinal = known_symbols[hole.symbol]
+                else:
+                    ordinal = len(known_symbols)
+                    known_symbols[hole.symbol] = ordinal
+                self._trampolines.add(ordinal)
+                hole.addend = ordinal
+                hole.symbol = None
         self.code.remove_jump()
         self.code.add_nops(nop=nop, alignment=alignment)
         self.data.pad(8)
Index: Python-3.14.3/Tools/jit/_targets.py
===================================================================
--- Python-3.14.3.orig/Tools/jit/_targets.py    2026-03-28 19:07:01.513403213 
+0100
+++ Python-3.14.3/Tools/jit/_targets.py 2026-03-28 19:07:08.906085812 +0100
@@ -150,10 +150,6 @@
             "-fno-asynchronous-unwind-tables",
             # Don't call built-in functions that we can't find or patch:
             "-fno-builtin",
-            # Emit relaxable 64-bit calls/jumps, so we don't have to worry 
about
-            # about emitting in-range trampolines for out-of-range targets.
-            # We can probably remove this and emit trampolines in the future:
-            "-fno-plt",
             # Don't call stack-smashing canaries that we can't find or patch:
             "-fno-stack-protector",
             "-std=c11",
@@ -523,7 +519,7 @@
         condition = "defined(__aarch64__) && defined(__APPLE__)"
         target = _MachO(host, condition, alignment=8, prefix="_")
     elif re.fullmatch(r"aarch64-pc-windows-msvc", host):
-        args = ["-fms-runtime-lib=dll", "-fplt"]
+        args = ["-fms-runtime-lib=dll"]
         condition = "defined(_M_ARM64)"
         target = _COFF(host, condition, alignment=8, args=args)
     elif re.fullmatch(r"aarch64-.*-linux-gnu", host):
@@ -532,6 +528,7 @@
             # On aarch64 Linux, intrinsics were being emitted and this flag
             # was required to disable them.
             "-mno-outline-atomics",
+            "-fno-plt",
         ]
         condition = "defined(__aarch64__) && defined(__linux__)"
         target = _ELF(host, condition, alignment=8, args=args)
@@ -551,7 +548,7 @@
         condition = "defined(_M_X64)"
         target = _COFF(host, condition, args=args)
     elif re.fullmatch(r"x86_64-.*-linux-gnu", host):
-        args = ["-fno-pic", "-mcmodel=medium", "-mlarge-data-threshold=0"]
+        args = ["-fno-pic", "-mcmodel=medium", "-mlarge-data-threshold=0", 
"-fno-plt"]
         condition = "defined(__x86_64__) && defined(__linux__)"
         target = _ELF(host, condition, args=args)
     else:

++++++ build.specials.obscpio ++++++
--- old/.gitignore      2026-03-16 14:37:41.000000000 +0100
+++ new/.gitignore      2026-03-28 19:09:38.000000000 +0100
@@ -2,5 +2,4 @@
 *.obscpio
 _build.*
 .pbuild
-*.orig
 python314-*-build/

++++++ 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-03-28 19:09:38.000000000 +0100
@@ -0,0 +1,5 @@
+.osc
+*.obscpio
+_build.*
+.pbuild
+python314-*-build/

Reply via email to