Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python313 for openSUSE:Factory 
checked in at 2026-08-17 16:55:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python313 (Old)
 and      /work/SRC/openSUSE:Factory/.python313.new.1258 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python313"

Mon Aug 17 16:55:28 2026 rev:44 rq:1371267 version:3.13.14

Changes:
--------
--- /work/SRC/openSUSE:Factory/python313/python313.changes      2026-07-22 
19:01:14.229095898 +0200
+++ /work/SRC/openSUSE:Factory/.python313.new.1258/python313.changes    
2026-08-17 16:55:30.414666548 +0200
@@ -1,0 +2,33 @@
+Fri Aug  7 07:23:53 UTC 2026 - Matej Cepl <[email protected]>
+
+- Add reproducible_stencils.patch from gh#python/cpython!154988
+
+-------------------------------------------------------------------
+Fri Jul 31 13:35:09 UTC 2026 - Matej Cepl <[email protected]>
+
+- CVE-2026-0864: Normalize all line endings (CR, CRLF, and LF) in
+  configparser (bsc#1269066, gh#python/cpython#143927)
+  CVE-2026-0864-normalize-LFTAB-configparser.patch
+
+-------------------------------------------------------------------
+Fri Jul 31 13:22:45 UTC 2026 - Matej Cepl <[email protected]>
+
+- CVE-2026-11972: Make tarfile._Stream.seek break at EOF
+  (bsc#1269788, gh-151981)
+  CVE-2026-11972-tarfile-Stream-seek-EOF.patch
+
+-------------------------------------------------------------------
+Fri Jul 31 13:07:50 UTC 2026 - Matej Cepl <[email protected]>
+
+- CVE-2026-4360: Pass filter_function to TarFile._extract_one()
+  during .extract() (bsc#1269959, gh#python/cpython#151987)
+  CVE-2026-4360-filter_function-TarFile-extractone.patch
+
+-------------------------------------------------------------------
+Fri Jul 31 12:54:51 UTC 2026 - Matej Cepl <[email protected]>
+
+- CVE-2026-15308: Fix quadratic complexity in incremental parsing
+  in HTMLParser (bsc#1271192, gh#python/cpython#153030)
+  CVE-2026-15308-HTMLParser-CPU-exhaust.patch
+
+-------------------------------------------------------------------
@@ -5 +38,2 @@
-  hardlink-extraction fallback (bsc#1268977)
+  hardlink-extraction fallback (bsc#1268977,
+  gh#python/cpython#151558)
@@ -52 +86 @@
-      extraction filter.
+      extraction filter (bsc#1267821, CVE-2026-7774).
@@ -59 +93,2 @@
-      a linear-time counting sort for long runs.
+      a linear-time counting sort for long runs (bsc#1267581,
+      CVE-2026-3276).
@@ -96,0 +132,5 @@
+    - gh-149144: Fixes an issue introduced by CVE-2026-6019
+      patch. atob() in JavaScript processes bytes as 'latin-1',
+      switches the encoding algorithm from base64 to percent
+      encoding and uses the decodeURIComponent() JavaScript API
+      (bsc#1263083).

New:
----
  CVE-2026-0864-normalize-LFTAB-configparser.patch
  CVE-2026-11972-tarfile-Stream-seek-EOF.patch
  CVE-2026-15308-HTMLParser-CPU-exhaust.patch
  CVE-2026-4360-filter_function-TarFile-extractone.patch
  reproducible_stencils.patch

----------(New B)----------
  New:  configparser (bsc#1269066, gh#python/cpython#143927)
  CVE-2026-0864-normalize-LFTAB-configparser.patch
  New:  (bsc#1269788, gh-151981)
  CVE-2026-11972-tarfile-Stream-seek-EOF.patch
  New:  in HTMLParser (bsc#1271192, gh#python/cpython#153030)
  CVE-2026-15308-HTMLParser-CPU-exhaust.patch
  New:  during .extract() (bsc#1269959, gh#python/cpython#151987)
  CVE-2026-4360-filter_function-TarFile-extractone.patch
  New:
- Add reproducible_stencils.patch from gh#python/cpython!154988
----------(New E)----------

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

Other differences:
------------------
++++++ python313.spec ++++++
--- /var/tmp/diff_new_pack.guDIG7/_old  2026-08-17 16:55:33.256766584 +0200
+++ /var/tmp/diff_new_pack.guDIG7/_new  2026-08-17 16:55:33.258766655 +0200
@@ -248,6 +248,21 @@
 # PATCH-FIX-UPSTREAM CVE-2026-11940-tarfile-escape.patch bsc#1268977 
[email protected]
 # Fix symlink escape via tarfile hardlink-extraction fallback
 Patch56:        CVE-2026-11940-tarfile-escape.patch
+# PATCH-FIX-UPSTREAM reproducible_stencils.patch gh#python/cpython!154988 
[email protected]
+# make jit_stencils.h reproducible
+Patch57:        reproducible_stencils.patch
+# PATCH-FIX-UPSTREAM CVE-2026-0864-normalize-LFTAB-configparser.patch 
bsc#1269066 [email protected]
+# Normalize all line endings (CR, CRLF, and LF) in configparser
+Patch58:        CVE-2026-0864-normalize-LFTAB-configparser.patch
+# PATCH-FIX-UPSTREAM CVE-2026-11972-tarfile-Stream-seek-EOF.patch bsc#1269788 
[email protected]
+# Make tarfile._Stream.seek break at EOF
+Patch59:        CVE-2026-11972-tarfile-Stream-seek-EOF.patch
+# PATCH-FIX-UPSTREAM CVE-2026-4360-filter_function-TarFile-extractone.patch 
bsc#1269959 [email protected]
+# Pass filter_function to TarFile._extract_one() during .extract()
+Patch60:        CVE-2026-4360-filter_function-TarFile-extractone.patch
+# PATCH-FIX-UPSTREAM CVE-2026-15308-HTMLParser-CPU-exhaust.patch bsc#1271192 
[email protected]
+# Fix quadratic complexity in incremental parsing in HTMLParser
+Patch61:        CVE-2026-15308-HTMLParser-CPU-exhaust.patch
 #### END OF PATCHES
 BuildRequires:  autoconf-archive
 BuildRequires:  automake

++++++ CVE-2026-0864-normalize-LFTAB-configparser.patch ++++++
>From f6a86b1ac9aeca3b48173ed3f88d8c097ec5131d Mon Sep 17 00:00:00 2001
From: Seth Larson <[email protected]>
Date: Tue, 23 Jun 2026 08:33:51 -0500
Subject: [PATCH] gh-143927: Normalize all line endings (CR, CRLF, and LF) in
 configparser (GH-143929) (cherry picked from commit
 5858e42c539dac8394636a6e9b30472b8994851f)

Co-authored-by: Seth Larson <[email protected]>
---
 Lib/configparser.py                                                      |    
4 ++-
 Lib/test/test_configparser.py                                            |   
11 ++++++++++
 Misc/NEWS.d/next/Security/2026-01-16-11-58-19.gh-issue-143927.aviFeG.rst |    
2 +
 3 files changed, 16 insertions(+), 1 deletion(-)
 create mode 100644 
Misc/NEWS.d/next/Security/2026-01-16-11-58-19.gh-issue-143927.aviFeG.rst

Index: Python-3.13.14/Lib/configparser.py
===================================================================
--- Python-3.13.14.orig/Lib/configparser.py     2026-06-10 14:24:04.000000000 
+0200
+++ Python-3.13.14/Lib/configparser.py  2026-08-09 22:28:22.232886394 +0200
@@ -973,7 +973,9 @@
             value = self._interpolation.before_write(self, section_name, key,
                                                      value)
             if value is not None or not self._allow_no_value:
-                value = delimiter + str(value).replace('\n', '\n\t')
+                # Convert all possible line-endings into '\n\t'
+                value = (delimiter + str(value).replace('\r\n', '\n')
+                         .replace('\r', '\n').replace('\n', '\n\t'))
             else:
                 value = ""
             fp.write("{}{}\n".format(key, value))
Index: Python-3.13.14/Lib/test/test_configparser.py
===================================================================
--- Python-3.13.14.orig/Lib/test/test_configparser.py   2026-06-10 
14:24:04.000000000 +0200
+++ Python-3.13.14/Lib/test/test_configparser.py        2026-08-09 
22:28:22.233186370 +0200
@@ -526,6 +526,17 @@
             cf.get(self.default_section, "Foo"), "Bar",
             "could not locate option, expecting case-insensitive defaults")
 
+    def test_crlf_normalization(self):
+        cf = self.newconfig({"key1": "a\nb","key2": "a\rb", "key3": "a\r\nb", 
"key4": "a\r\nb"})
+        buf = io.StringIO()
+        cf.write(buf)
+        cf_str = buf.getvalue()
+        self.assertNotIn("\r", cf_str)
+        self.assertNotIn("\r\n", cf_str)
+        self.assertEqual(cf_str.count("\n"), 10)
+        self.assertEqual(cf_str.count("\n\t"), 4)
+        self.assertTrue(cf_str.endswith("\n\n"))
+
     def test_parse_errors(self):
         cf = self.newconfig()
         self.parse_error(cf, configparser.ParsingError,
Index: 
Python-3.13.14/Misc/NEWS.d/next/Security/2026-01-16-11-58-19.gh-issue-143927.aviFeG.rst
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ 
Python-3.13.14/Misc/NEWS.d/next/Security/2026-01-16-11-58-19.gh-issue-143927.aviFeG.rst
     2026-08-09 22:28:22.233463081 +0200
@@ -0,0 +1,2 @@
+Normalize all line endings (CR, CRLF, and LF) to LF+TAB when writing
+multi-line configparser values.

++++++ CVE-2026-11972-tarfile-Stream-seek-EOF.patch ++++++
>From 2fb7c785e2ce6d592b82be2840615611c8d3da4d Mon Sep 17 00:00:00 2001
From: Petr Viktorin <[email protected]>
Date: Tue, 23 Jun 2026 15:13:30 +0200
Subject: [PATCH] gh-151981: Make tarfile._Stream.seek break at EOF (GH-151982)
 (cherry picked from commit f50bf13566189c8d0ce5a814f33eff3d89951896)

Co-authored-by: Petr Viktorin <[email protected]>
Co-authored-by: Stan Ulbrych <[email protected]>
---
 Lib/tarfile.py                                                           |    
4 +-
 Lib/test/test_tarfile.py                                                 |   
16 ++++++++++
 Misc/NEWS.d/next/Security/2026-06-23-13-28-16.gh-issue-151981.xBHEcU.rst |    
2 +
 3 files changed, 21 insertions(+), 1 deletion(-)
 create mode 100644 
Misc/NEWS.d/next/Security/2026-06-23-13-28-16.gh-issue-151981.xBHEcU.rst

Index: Python-3.13.14/Lib/tarfile.py
===================================================================
--- Python-3.13.14.orig/Lib/tarfile.py  2026-08-09 23:17:17.509878117 +0200
+++ Python-3.13.14/Lib/tarfile.py       2026-08-09 23:17:17.554905835 +0200
@@ -515,7 +515,9 @@
         if pos - self.pos >= 0:
             blocks, remainder = divmod(pos - self.pos, self.bufsize)
             for i in range(blocks):
-                self.read(self.bufsize)
+                data = self.read(self.bufsize)
+                if not data:
+                    break
             self.read(remainder)
         else:
             raise StreamError("seeking backwards is not allowed")
Index: Python-3.13.14/Lib/test/test_tarfile.py
===================================================================
--- Python-3.13.14.orig/Lib/test/test_tarfile.py        2026-08-09 
23:17:17.510736636 +0200
+++ Python-3.13.14/Lib/test/test_tarfile.py     2026-08-09 23:17:17.555562714 
+0200
@@ -4762,6 +4762,22 @@
         with self.check_context(arc.open(errorlevel='boo!'), 
filtererror_filter):
             self.expect_exception(TypeError)  # errorlevel is not int
 
+    @support.subTests('format', [tarfile.GNU_FORMAT, tarfile.PAX_FORMAT])
+    def test_getmembers_big_size(self, format):
+        # gh-151981: A loop in seek() for streaming files tried to read the
+        # declared number of blocks even at EOF
+        tinfo = tarfile.TarInfo("huge-file")
+        tinfo.size = 1 << 64
+        bio = io.BytesIO()
+        # Write header without data
+        bio.write(tinfo.tobuf(format))
+
+        # Reset & try to get contents
+        bio.seek(0)
+        with tarfile.open(fileobj=bio, mode="r|") as tar:
+            with self.assertRaises(tarfile.ReadError):
+                tar.getmembers()
+
 
 class OverwriteTests(archiver_tests.OverwriteTests, unittest.TestCase):
     testdir = os.path.join(TEMPDIR, "testoverwrite")
Index: 
Python-3.13.14/Misc/NEWS.d/next/Security/2026-06-23-13-28-16.gh-issue-151981.xBHEcU.rst
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ 
Python-3.13.14/Misc/NEWS.d/next/Security/2026-06-23-13-28-16.gh-issue-151981.xBHEcU.rst
     2026-08-09 23:17:17.556049943 +0200
@@ -0,0 +1,2 @@
+In :mod:`tarfile`, seeking a stream now stops when end of the stream is
+reached.

++++++ CVE-2026-15308-HTMLParser-CPU-exhaust.patch ++++++
>From fc4dca171f6bb07d9f4e73765ffef2ba10ab8b0b Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <[email protected]>
Date: Sat, 4 Jul 2026 20:40:22 +0300
Subject: [PATCH] gh-153030: Fix quadratic complexity in incremental parsing in
 HTMLParser (GH-153031)

When an unterminated construct (e.g. a tag or comment) spanned many
feed() calls, rescanning the growing buffer and concatenating new data
onto it were both quadratic.  New data is now accumulated in a list and
only joined and parsed once enough has piled up.
(cherry picked from commit bcf98ddbc40ec9b3ee87da0124a5660b19b7e606)

Co-authored-by: Serhiy Storchaka <[email protected]>
Co-Authored-By: Claude Opus 4.8 <[email protected]>
---
 Lib/html/parser.py                                                       |   
32 +++++++++-
 Lib/test/test_htmlparser.py                                              |   
20 ++++++
 Misc/NEWS.d/next/Security/2026-07-04-17-00-00.gh-issue-153030.RovkP6.rst |    
3 
 3 files changed, 53 insertions(+), 2 deletions(-)
 create mode 100644 
Misc/NEWS.d/next/Security/2026-07-04-17-00-00.gh-issue-153030.RovkP6.rst

Index: Python-3.13.14/Lib/html/parser.py
===================================================================
--- Python-3.13.14.orig/Lib/html/parser.py      2026-06-10 14:24:04.000000000 
+0200
+++ Python-3.13.14/Lib/html/parser.py   2026-08-09 23:36:47.005389358 +0200
@@ -157,6 +157,9 @@
         self.cdata_elem = None
         self._support_cdata = True
         self._escapable = True
+        self._pending = []
+        self._pending_len = 0
+        self._parse_threshold = 1
         super().reset()
 
     def feed(self, data):
@@ -165,11 +168,36 @@
         Call this as often as you want, with as little or as much text
         as you want (may include '\n').
         """
-        self.rawdata = self.rawdata + data
-        self.goahead(0)
+        # Accumulate new data in a list and only join and parse it once
+        # enough has piled up.  Rescanning an unparsed buffer (e.g. an
+        # unterminated tag) and concatenating onto it on every call would
+        # both be quadratic in the input size.
+        self._pending_len += len(data)
+        if self._pending_len < self._parse_threshold:
+            self._pending.append(data)
+        else:
+            if not self._pending:
+                self.rawdata += data
+            else:
+                self._pending.append(data)
+                self.rawdata += ''.join(self._pending)
+                self._pending.clear()
+            self._pending_len = 0
+            n = len(self.rawdata)
+            self.goahead(0)
+            if len(self.rawdata) < n:
+                # Some data was parsed; resume on the next call.
+                self._parse_threshold = 1
+            else:
+                # Nothing was parsed; wait until the buffer doubles.
+                self._parse_threshold = len(self.rawdata)
 
     def close(self):
         """Handle any buffered data."""
+        if self._pending:
+            self.rawdata += ''.join(self._pending)
+            self._pending.clear()
+            self._pending_len = 0
         self.goahead(1)
 
     __starttag_text = None
Index: Python-3.13.14/Lib/test/test_htmlparser.py
===================================================================
--- Python-3.13.14.orig/Lib/test/test_htmlparser.py     2026-06-10 
14:24:04.000000000 +0200
+++ Python-3.13.14/Lib/test/test_htmlparser.py  2026-08-09 23:36:47.005672372 
+0200
@@ -1031,6 +1031,26 @@
         check("<![CDATA[" * 9 * n)
         check("<!doctype" * 35 * n)
 
+    @support.requires_resource('cpu')
+    def test_incremental_no_quadratic_complexity(self):
+        # An unterminated construct fed in many small chunks used to take
+        # quadratic time, both to rescan and to concatenate the buffer.
+        # Now it takes a fraction of a second.
+        def check(prefix, chunk, suffix):
+            parser = html.parser.HTMLParser()
+            parser.feed(prefix)
+            for _ in range(200_000):
+                parser.feed(chunk)
+            parser.feed(suffix)
+            parser.close()
+        chunk = "a" * 64
+        check("<!--", chunk, "-->")       # comment
+        check("<?", chunk, ">")           # processing instruction
+        check("<!doctype ", chunk, ">")   # doctype
+        check("<![CDATA[", chunk, "]]>")  # CDATA section
+        check("<a href='", chunk, "'>")   # start tag
+        check("<script>", chunk, "</script>")  # RAWTEXT element
+
 
 class AttributesTestCase(TestCaseBase):
 
Index: 
Python-3.13.14/Misc/NEWS.d/next/Security/2026-07-04-17-00-00.gh-issue-153030.RovkP6.rst
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ 
Python-3.13.14/Misc/NEWS.d/next/Security/2026-07-04-17-00-00.gh-issue-153030.RovkP6.rst
     2026-08-09 23:36:47.005873451 +0200
@@ -0,0 +1,3 @@
+Fixed quadratic complexity in incremental parsing of long unterminated
+constructs (such as tags or comments) in :class:`html.parser.HTMLParser`,
+which could be exploited for a denial of service.

++++++ CVE-2026-4360-filter_function-TarFile-extractone.patch ++++++
>From e1e764993cf5817936e50c294dc954b5be201781 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <[email protected]>
Date: Mon, 29 Jun 2026 17:41:26 +0200
Subject: [PATCH] gh-151987: Pass filter_function to TarFile._extract_one()
 during .extract() (GH-151988)

(cherry picked from commit 7ccdbaba2c54250a70d7f25632152df7655a5e0a)

Co-authored-by: Petr Viktorin <[email protected]>
Co-authored-by: Seth Michael Larson <[email protected]>
---
 Lib/tarfile.py                                                           |    
3 
 Lib/test/test_tarfile.py                                                 |   
92 ++++++++++
 Misc/NEWS.d/next/Security/2026-06-23-14-19-30.gh-issue-151987.8mNIMf.rst |    
2 
 3 files changed, 96 insertions(+), 1 deletion(-)
 create mode 100644 
Misc/NEWS.d/next/Security/2026-06-23-14-19-30.gh-issue-151987.8mNIMf.rst

Index: Python-3.13.14/Lib/tarfile.py
===================================================================
--- Python-3.13.14.orig/Lib/tarfile.py  2026-08-09 23:33:55.420853456 +0200
+++ Python-3.13.14/Lib/tarfile.py       2026-08-09 23:33:55.436421166 +0200
@@ -2449,7 +2449,8 @@
         tarinfo, unfiltered = self._get_extract_tarinfo(
             member, filter_function, path)
         if tarinfo is not None:
-            self._extract_one(tarinfo, path, set_attrs, numeric_owner)
+            self._extract_one(tarinfo, path, set_attrs, numeric_owner,
+                              filter_function=filter_function)
 
     def _get_extract_tarinfo(self, member, filter_function, path):
         """Get (filtered, unfiltered) TarInfos from *member*
Index: Python-3.13.14/Lib/test/test_tarfile.py
===================================================================
--- Python-3.13.14.orig/Lib/test/test_tarfile.py        2026-08-09 
23:33:55.421831219 +0200
+++ Python-3.13.14/Lib/test/test_tarfile.py     2026-08-09 23:33:55.438126494 
+0200
@@ -4390,6 +4390,98 @@
                     st_mode = cc.outerdir.stat().st_mode
                     self.assertNotEqual(st_mode & 0o777, 0o777)
 
+    @symlink_test
+    @unittest.skipUnless(hasattr(os, 'chown'), "missing os.chown")
+    @unittest.skipUnless(hasattr(os, 'lchown'), "missing os.lchown")
+    @unittest.skipUnless(hasattr(os, 'geteuid'), "missing os.geteuid")
+    @support.subTests('link_type', (tarfile.SYMTYPE, tarfile.LNKTYPE))
+    def test_chown_links_on_extract(self, link_type):
+        with ArchiveMaker() as arc:
+            arc.add("test.txt",
+                    uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x')
+            arc.add("link",
+                    type=link_type,
+                    linkname='test.txt',
+                    uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x')
+
+        with (
+            os_helper.temp_dir() as tmpdir,
+            arc.open() as tar,
+            unittest.mock.patch("os.chown") as mock_chown,
+            unittest.mock.patch("os.lchown") as mock_lchown,
+            unittest.mock.patch("os.geteuid") as mock_geteuid,
+        ):
+            # Set UID to 0 so chown() is attempted.
+            mock_geteuid.return_value = 0
+            tar.extract("link", path=tmpdir, filter='data')
+            extract_path = os.path.join(tmpdir, "link")
+
+            if link_type == tarfile.SYMTYPE:
+                mock_chown.assert_not_called()
+                mock_lchown.assert_called_once_with(extract_path, -1, -1)
+            else:
+                mock_chown.assert_has_calls([
+                    unittest.mock.call(extract_path, -1, -1),
+                    unittest.mock.call(extract_path, -1, -1)
+                ])
+                mock_lchown.assert_not_called()
+
+    @symlink_test
+    @unittest.skipUnless(hasattr(os, 'chown'), "missing os.chown")
+    @unittest.skipUnless(hasattr(os, 'lchown'), "missing os.lchown")
+    @unittest.skipUnless(hasattr(os, 'geteuid'), "missing os.geteuid")
+    @support.subTests('link_type', (tarfile.SYMTYPE, tarfile.LNKTYPE))
+    def test_chown_links_on_extractall(self, link_type):
+        with ArchiveMaker() as arc:
+            arc.add("test.txt",
+                    uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x')
+            arc.add("link",
+                    type=link_type,
+                    linkname='test.txt',
+                    uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x')
+
+        with (
+            os_helper.temp_dir() as tmpdir,
+            arc.open() as tar,
+            unittest.mock.patch("os.chown") as mock_chown,
+            unittest.mock.patch("os.lchown") as mock_lchown,
+            unittest.mock.patch("os.geteuid") as mock_geteuid,
+        ):
+            # Set UID to 0 so chown() is attempted.
+            mock_geteuid.return_value = 0
+            tar.extractall(path=tmpdir, filter='data')
+            extract_link_path = os.path.join(tmpdir, "link")
+            extract_file_path = os.path.join(tmpdir, "test.txt")
+
+            if link_type == tarfile.SYMTYPE:
+                mock_chown.assert_called_once_with(extract_file_path, -1, -1)
+                mock_lchown.assert_called_once_with(extract_link_path, -1, -1)
+            else:
+                mock_chown.assert_has_calls([
+                    unittest.mock.call(extract_file_path, -1, -1),
+                    unittest.mock.call(extract_link_path, -1, -1)
+                ])
+                mock_lchown.assert_not_called()
+
+    def test_extract_filters_target(self):
+        # Test that when extract() falls back to extracting (rather than
+        # linking) a hardlink target, it filters the target.
+        with ArchiveMaker() as arc:
+            arc.add("target")
+            arc.add("link", hardlink_to="target")
+        def testing_filter(member, path):
+            if member.name == 'target':
+                # target: set read-only
+                return member.replace(mode=stat.S_IRUSR)
+            # link: don't overwrite the mode
+            return member.replace(mode=None)
+        tempdir = pathlib.Path(TEMPDIR) / 'extract'
+        with os_helper.temp_dir(tempdir), arc.open() as tar:
+            tar.extract("link", path=tempdir, filter=testing_filter)
+            path = tempdir / 'link'
+            if os_helper.can_chmod():
+                self.assertFalse(path.stat().st_mode & stat.S_IWUSR)
+
     def test_link_fallback_normalizes(self):
         # Make sure hardlink fallbacks work for non-normalized paths for all
         # filters
Index: 
Python-3.13.14/Misc/NEWS.d/next/Security/2026-06-23-14-19-30.gh-issue-151987.8mNIMf.rst
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ 
Python-3.13.14/Misc/NEWS.d/next/Security/2026-06-23-14-19-30.gh-issue-151987.8mNIMf.rst
     2026-08-09 23:33:55.438660200 +0200
@@ -0,0 +1,2 @@
+The :meth:`tarfile.TarFile.extract` method now applies the given filter when
+it extracts a link target from the archive as a fallback.

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.guDIG7/_old  2026-08-17 16:55:33.460773765 +0200
+++ /var/tmp/diff_new_pack.guDIG7/_new  2026-08-17 16:55:33.466773976 +0200
@@ -1,6 +1,6 @@
-mtime: 1784282806
-commit: 231e2440e2990865abbe4e28761b76d134fc48b59de16e0a1f93c13ed00e4c59
+mtime: 1786748532
+commit: 0c31b87f057215535b70ee18ac6609824e74d568e05b74cdef7d75f0af347233
 url: https://src.opensuse.org/python-interpreters/python313
-revision: 231e2440e2990865abbe4e28761b76d134fc48b59de16e0a1f93c13ed00e4c59
+revision: 0c31b87f057215535b70ee18ac6609824e74d568e05b74cdef7d75f0af347233
 projectscmsync: https://src.opensuse.org/python-interpreters/_ObsPrj
 

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-08-15 01:02:12.000000000 +0200
@@ -0,0 +1,6 @@
+.osc
+*.obscpio
+*.osc
+_build.*
+.pbuild
+python313-*-build/


++++++ reproducible_stencils.patch ++++++
>From eb1d1079dcf13fa601bf95b40f65a0b0dee5f5ff Mon Sep 17 00:00:00 2001
From: "Bernhard M. Wiedemann" <[email protected]>
Date: Thu, 30 Jul 2026 21:27:28 +0200
Subject: [PATCH] [3.13] GH-115869: Make jit_stencils.h reproducible
 (GH-127166)

Keep the build tmpdir out of jit_stencils.h

llvm-objdump prints the path of the object file it disassembles, and the
JIT builds every stencil in a fresh tempfile.TemporaryDirectory().  That
random path ended up verbatim in the disassembly comments of the
generated jit_stencils.h, so two builds of the same source never produced
the same header:

     // /tmp/tmp164olvmi/_BINARY_OP.o:  file format elf64-x86-64
    +// /tmp/tmpmufvewt2/_BINARY_OP.o:  file format elf64-x86-64

Replace the full path with the bare file name.

Only the _parse() hunk of that commit is taken here; the rest of it
depends on refactorings that are not in 3.13.

(cherry picked from commit 17c16aea66b606d66f71ae9af381bc34d0ef3f5f)

Co-authored-by: Brandt Bucher <[email protected]>
---
 .../next/Build/2024-11-22-08-46-46.gh-issue-115869.UVLSKd.rst | 1 +
 Tools/jit/_targets.py                                         | 4 +++-
 2 files changed, 4 insertions(+), 1 deletion(-)
 create mode 100644 
Misc/NEWS.d/next/Build/2024-11-22-08-46-46.gh-issue-115869.UVLSKd.rst

diff --git 
a/Misc/NEWS.d/next/Build/2024-11-22-08-46-46.gh-issue-115869.UVLSKd.rst 
b/Misc/NEWS.d/next/Build/2024-11-22-08-46-46.gh-issue-115869.UVLSKd.rst
new file mode 100644
index 000000000000000..9e8a078983f20b5
--- /dev/null
+++ b/Misc/NEWS.d/next/Build/2024-11-22-08-46-46.gh-issue-115869.UVLSKd.rst
@@ -0,0 +1 @@
+Make ``jit_stencils.h`` (which is produced during JIT builds) reproducible.
diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py
index c50bd63545c2fe4..55f2332136cba92 100644
--- a/Tools/jit/_targets.py
+++ b/Tools/jit/_targets.py
@@ -65,8 +65,10 @@ async def _parse(self, path: pathlib.Path) -> 
_stencils.StencilGroup:
         args = ["--disassemble", "--reloc", f"{path}"]
         output = await _llvm.maybe_run("llvm-objdump", args, echo=self.verbose)
         if output is not None:
+            # Make sure that full paths don't leak out (for reproducibility):
+            long, short = str(path), str(path.name)
             group.code.disassembly.extend(
-                line.expandtabs().strip()
+                line.expandtabs().strip().replace(long, short)
                 for line in output.splitlines()
                 if not line.isspace()
             )

Reply via email to