Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python315 for openSUSE:Factory 
checked in at 2025-12-01 11:12:33
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python315 (Old)
 and      /work/SRC/openSUSE:Factory/.python315.new.14147 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python315"

Mon Dec  1 11:12:33 2025 rev:2 rq:1320571 version:3.15.0~a1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python315/python315.changes      2025-11-07 
18:26:34.065805124 +0100
+++ /work/SRC/openSUSE:Factory/.python315.new.14147/python315.changes   
2025-12-01 11:12:40.904883450 +0100
@@ -1,0 +2,15 @@
+Thu Nov 13 17:13:03 UTC 2025 - Matej Cepl <[email protected]>
+
+- Add CVE-2025-6075-expandvars-perf-degrad.patch avoid simple
+  quadratic complexity vulnerabilities of os.path.expandvars()
+  (CVE-2025-6075, bsc#1252974).
+- Remove CVE-2025-8291-consistency-zip64.patch … the fix has been
+  already included in 3.15.0~a1.
+- Skip test_curses on ppc64le (gh#python/cpython#141534)
+
+-------------------------------------------------------------------
+Mon Nov 10 10:01:37 UTC 2025 - Andreas Schwab <[email protected]>
+
+- Update list of skipped tests in qemu linux-user emulation
+
+-------------------------------------------------------------------
@@ -87 +102,2 @@
-      data” if there are no bytes prepended to the ZIP file.
+      data” if there are no bytes prepended to the ZIP file
+      (bsc#1251305, CVE-2025-8291).

Old:
----
  CVE-2025-8291-consistency-zip64.patch

New:
----
  CVE-2025-6075-expandvars-perf-degrad.patch

----------(Old B)----------
  Old:  (CVE-2025-6075, bsc#1252974).
- Remove CVE-2025-8291-consistency-zip64.patch … the fix has been
  already included in 3.15.0~a1.
----------(Old E)----------

----------(New B)----------
  New:
- Add CVE-2025-6075-expandvars-perf-degrad.patch avoid simple
  quadratic complexity vulnerabilities of os.path.expandvars()
----------(New E)----------

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

Other differences:
------------------
++++++ python315.spec ++++++
--- /var/tmp/diff_new_pack.dpaXd7/_old  2025-12-01 11:12:42.804963823 +0100
+++ /var/tmp/diff_new_pack.dpaXd7/_new  2025-12-01 11:12:42.808963992 +0100
@@ -224,9 +224,9 @@
 Patch41:        bsc1243155-sphinx-non-determinism.patch
 # PATCH-FIX-OPENSUSE gh139257-Support-docutils-0.22.patch 
gh#python/cpython#139257 [email protected]
 Patch42:        gh139257-Support-docutils-0.22.patch
-# PATCH-FIX-UPSTREAM CVE-2025-8291-consistency-zip64.patch bsc#1251305 
[email protected]
-# Check consistency of the zip64 end of central directory record
-Patch43:        CVE-2025-8291-consistency-zip64.patch
+# PATCH-FIX-UPSTREAM CVE-2025-6075-expandvars-perf-degrad.patch bsc#1252974 
[email protected]
+# Avoid potential quadratic complexity vulnerabilities in path modules
+Patch43:        CVE-2025-6075-expandvars-perf-degrad.patch
 #### Python 3.15 DEVELOPMENT PATCHES
 BuildRequires:  autoconf-archive
 BuildRequires:  automake
@@ -664,22 +664,20 @@
 EXCLUDE="$EXCLUDE test_multiprocessing_forkserver"
 %endif
 %ifarch ppc ppc64 ppc64le
-# exclue test_faulthandler due to bnc#831629
+# exclude test_faulthandler due to bnc#831629
 EXCLUDE="$EXCLUDE test_faulthandler"
+# exclude test_curse for gh#python/cpython#141534
+EXCLUDE="$EXCLUDE test_curses"
 %endif
 # some tests break in QEMU
 %if 0%{?qemu_user_space_build}
-# test_external_inspection: qemu does not support ptrace in test_self_trace
 # test_faulthandler: test_register_chain is racy
-# test_multiprocessing_spawn: qemu does not support CLONE_VFORK
-# test_os: test_fork_warns_when_non_python_thread_exists fails
 # test_posix: qemu does not support fexecve with O_CLOEXEC in test_fexecve
-# test_remote_pdb: qemu does not support process_vm_readv/writev
+# test_profiling: test_esrch_signal_handling times out
 # test_signal: qemu crashes in test_stress_modifying_handlers
 # test_socket: many CmsgTrunc tests fail
 # test_subprocess: qemu does not support CLONE_VFORK
-# test_sys: qemu does not support process_vm_readv/writev
-EXCLUDE="$EXCLUDE test_external_inspection test_faulthandler 
test_multiprocessing_spawn test_os test_posix test_remote_pdb test_signal 
test_socket test_subprocess test_sys"
+EXCLUDE="$EXCLUDE test_faulthandler test_posix test_profiling test_signal 
test_socket test_subprocess"
 %endif
 
 # This test (part of test_uuid) requires real network interfaces

++++++ CVE-2025-6075-expandvars-perf-degrad.patch ++++++
>From e3b2c85d567b51dd84d1faf83398e97c0bf1eb60 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <[email protected]>
Date: Fri, 30 May 2025 22:33:31 +0300
Subject: [PATCH 1/2] gh-134873: Fix quadratic complexity in
 os.path.expandvars()

---
 Lib/ntpath.py                                                            |  
126 +++-------
 Lib/posixpath.py                                                         |   
43 +--
 Lib/test/test_genericpath.py                                             |   
21 +
 Lib/test/test_ntpath.py                                                  |   
22 +
 Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst |    
1 
 5 files changed, 97 insertions(+), 116 deletions(-)
 create mode 100644 
Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-134873.bu337o.rst

Index: Python-3.15.0a1/Lib/ntpath.py
===================================================================
--- Python-3.15.0a1.orig/Lib/ntpath.py  2025-10-14 12:46:08.000000000 +0200
+++ Python-3.15.0a1/Lib/ntpath.py       2025-11-13 18:28:37.445868967 +0100
@@ -400,17 +400,23 @@
 # XXX With COMMAND.COM you can use any characters in a variable name,
 # XXX except '^|<>='.
 
+_varpattern = r"'[^']*'?|%(%|[^%]*%?)|\$(\$|[-\w]+|\{[^}]*\}?)"
+_varsub = None
+_varsubb = None
+
 def expandvars(path):
     """Expand shell variables of the forms $var, ${var} and %var%.
 
     Unknown variables are left unchanged."""
     path = os.fspath(path)
+    global _varsub, _varsubb
     if isinstance(path, bytes):
         if b'$' not in path and b'%' not in path:
             return path
-        import string
-        varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii')
-        quote = b'\''
+        if not _varsubb:
+            import re
+            _varsubb = re.compile(_varpattern.encode(), re.ASCII).sub
+        sub = _varsubb
         percent = b'%'
         brace = b'{'
         rbrace = b'}'
@@ -419,94 +425,44 @@
     else:
         if '$' not in path and '%' not in path:
             return path
-        import string
-        varchars = string.ascii_letters + string.digits + '_-'
-        quote = '\''
+        if not _varsub:
+            import re
+            _varsub = re.compile(_varpattern, re.ASCII).sub
+        sub = _varsub
         percent = '%'
         brace = '{'
         rbrace = '}'
         dollar = '$'
         environ = os.environ
-    res = path[:0]
-    index = 0
-    pathlen = len(path)
-    while index < pathlen:
-        c = path[index:index+1]
-        if c == quote:   # no expansion within single quotes
-            path = path[index + 1:]
-            pathlen = len(path)
-            try:
-                index = path.index(c)
-                res += c + path[:index + 1]
-            except ValueError:
-                res += c + path
-                index = pathlen - 1
-        elif c == percent:  # variable or '%'
-            if path[index + 1:index + 2] == percent:
-                res += c
-                index += 1
-            else:
-                path = path[index+1:]
-                pathlen = len(path)
-                try:
-                    index = path.index(percent)
-                except ValueError:
-                    res += percent + path
-                    index = pathlen - 1
-                else:
-                    var = path[:index]
-                    try:
-                        if environ is None:
-                            value = os.fsencode(os.environ[os.fsdecode(var)])
-                        else:
-                            value = environ[var]
-                    except KeyError:
-                        value = percent + var + percent
-                    res += value
-        elif c == dollar:  # variable or '$$'
-            if path[index + 1:index + 2] == dollar:
-                res += c
-                index += 1
-            elif path[index + 1:index + 2] == brace:
-                path = path[index+2:]
-                pathlen = len(path)
-                try:
-                    index = path.index(rbrace)
-                except ValueError:
-                    res += dollar + brace + path
-                    index = pathlen - 1
-                else:
-                    var = path[:index]
-                    try:
-                        if environ is None:
-                            value = os.fsencode(os.environ[os.fsdecode(var)])
-                        else:
-                            value = environ[var]
-                    except KeyError:
-                        value = dollar + brace + var + rbrace
-                    res += value
-            else:
-                var = path[:0]
-                index += 1
-                c = path[index:index + 1]
-                while c and c in varchars:
-                    var += c
-                    index += 1
-                    c = path[index:index + 1]
-                try:
-                    if environ is None:
-                        value = os.fsencode(os.environ[os.fsdecode(var)])
-                    else:
-                        value = environ[var]
-                except KeyError:
-                    value = dollar + var
-                res += value
-                if c:
-                    index -= 1
+
+    def repl(m):
+        lastindex = m.lastindex
+        if lastindex is None:
+            return m[0]
+        name = m[lastindex]
+        if lastindex == 1:
+            if name == percent:
+                return name
+            if not name.endswith(percent):
+                return m[0]
+            name = name[:-1]
         else:
-            res += c
-        index += 1
-    return res
+            if name == dollar:
+                return name
+            if name.startswith(brace):
+                if not name.endswith(rbrace):
+                    return m[0]
+                name = name[1:-1]
+
+        try:
+            if environ is None:
+                return os.fsencode(os.environ[os.fsdecode(name)])
+            else:
+                return environ[name]
+        except KeyError:
+            return m[0]
+
+    return sub(repl, path)
 
 
 # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
Index: Python-3.15.0a1/Lib/posixpath.py
===================================================================
--- Python-3.15.0a1.orig/Lib/posixpath.py       2025-10-14 12:46:08.000000000 
+0200
+++ Python-3.15.0a1/Lib/posixpath.py    2025-11-13 18:28:37.446168939 +0100
@@ -285,42 +285,41 @@
 # This expands the forms $variable and ${variable} only.
 # Non-existent variables are left unchanged.
 
-_varprog = None
-_varprogb = None
+_varpattern = r'\$(\w+|\{[^}]*\}?)'
+_varsub = None
+_varsubb = None
 
 def expandvars(path):
     """Expand shell variables of form $var and ${var}.  Unknown variables
     are left unchanged."""
     path = os.fspath(path)
-    global _varprog, _varprogb
+    global _varsub, _varsubb
     if isinstance(path, bytes):
         if b'$' not in path:
             return path
-        if not _varprogb:
+        if not _varsubb:
             import re
-            _varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII)
-        search = _varprogb.search
+            _varsubb = re.compile(_varpattern.encode(), re.ASCII).sub
+        sub = _varsubb
         start = b'{'
         end = b'}'
         environ = getattr(os, 'environb', None)
     else:
         if '$' not in path:
             return path
-        if not _varprog:
+        if not _varsub:
             import re
-            _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII)
-        search = _varprog.search
+            _varsub = re.compile(_varpattern, re.ASCII).sub
+        sub = _varsub
         start = '{'
         end = '}'
         environ = os.environ
-    i = 0
-    while True:
-        m = search(path, i)
-        if not m:
-            break
-        i, j = m.span(0)
-        name = m.group(1)
-        if name.startswith(start) and name.endswith(end):
+
+    def repl(m):
+        name = m[1]
+        if name.startswith(start):
+            if not name.endswith(end):
+                return m[0]
             name = name[1:-1]
         try:
             if environ is None:
@@ -328,13 +327,11 @@
             else:
                 value = environ[name]
         except KeyError:
-            i = j
+            return m[0]
         else:
-            tail = path[j:]
-            path = path[:i] + value
-            i = len(path)
-            path += tail
-    return path
+            return value
+
+    return sub(repl, path)
 
 
 # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
Index: Python-3.15.0a1/Lib/test/test_genericpath.py
===================================================================
--- Python-3.15.0a1.orig/Lib/test/test_genericpath.py   2025-10-14 
12:46:08.000000000 +0200
+++ Python-3.15.0a1/Lib/test/test_genericpath.py        2025-11-13 
18:28:37.446403609 +0100
@@ -9,9 +9,9 @@
 import sys
 import unittest
 import warnings
-from test.support import (
-    is_apple, os_helper, warnings_helper
-)
+from test import support
+from test.support import os_helper
+from test.support import warnings_helper
 from test.support.script_helper import assert_python_ok
 from test.support.os_helper import FakePath
 
@@ -462,6 +462,19 @@
                   os.fsencode('$bar%s bar' % nonascii))
             check(b'$spam}bar', os.fsencode('%s}bar' % nonascii))
 
+    @support.requires_resource('cpu')
+    def test_expandvars_large(self):
+        expandvars = self.pathmodule.expandvars
+        with os_helper.EnvironmentVarGuard() as env:
+            env.clear()
+            env["A"] = "B"
+            n = 100_000
+            self.assertEqual(expandvars('$A'*n), 'B'*n)
+            self.assertEqual(expandvars('${A}'*n), 'B'*n)
+            self.assertEqual(expandvars('$A!'*n), 'B!'*n)
+            self.assertEqual(expandvars('${A}A'*n), 'BA'*n)
+            self.assertEqual(expandvars('${'*10*n), '${'*10*n)
+
     def test_abspath(self):
         self.assertIn("foo", self.pathmodule.abspath("foo"))
         with warnings.catch_warnings():
@@ -519,7 +532,7 @@
             # directory (when the bytes name is used).
             and sys.platform not in {
                 "win32", "emscripten", "wasi"
-            } and not is_apple
+            } and not support.is_apple
         ):
             name = os_helper.TESTFN_UNDECODABLE
         elif os_helper.TESTFN_NONASCII:
Index: Python-3.15.0a1/Lib/test/test_ntpath.py
===================================================================
--- Python-3.15.0a1.orig/Lib/test/test_ntpath.py        2025-10-14 
12:46:08.000000000 +0200
+++ Python-3.15.0a1/Lib/test/test_ntpath.py     2025-11-13 18:28:55.652664525 
+0100
@@ -9,7 +9,8 @@
 import warnings
 from ntpath import ALL_BUT_LAST, ALLOW_MISSING
 from test import support
-from test.support import TestFailed, cpython_only, os_helper
+from test import support
+from test.support import os_helper
 from test.support.os_helper import FakePath
 from test import test_genericpath
 from tempfile import TemporaryFile
@@ -59,7 +60,7 @@
     fn = fn.replace("\\", "\\\\")
     gotResult = eval(fn)
     if wantResult != gotResult and _norm(wantResult) != _norm(gotResult):
-        raise TestFailed("%s should return: %s but returned: %s" \
+        raise support.TestFailed("%s should return: %s but returned: %s" \
               %(str(fn), str(wantResult), str(gotResult)))
 
     # then with bytes
@@ -75,7 +76,7 @@
         warnings.simplefilter("ignore", DeprecationWarning)
         gotResult = eval(fn)
     if _norm(wantResult) != _norm(gotResult):
-        raise TestFailed("%s should return: %s but returned: %s" \
+        raise support.TestFailed("%s should return: %s but returned: %s" \
               %(str(fn), str(wantResult), repr(gotResult)))
 
 
@@ -1133,6 +1134,19 @@
             check('%spam%bar', '%sbar' % nonascii)
             check('%{}%bar'.format(nonascii), 'ham%sbar' % nonascii)
 
+    @support.requires_resource('cpu')
+    def test_expandvars_large(self):
+        expandvars = ntpath.expandvars
+        with os_helper.EnvironmentVarGuard() as env:
+            env.clear()
+            env["A"] = "B"
+            n = 100_000
+            self.assertEqual(expandvars('%A%'*n), 'B'*n)
+            self.assertEqual(expandvars('%A%A'*n), 'BA'*n)
+            self.assertEqual(expandvars("''"*n + '%%'), "''"*n + '%')
+            self.assertEqual(expandvars("%%"*n), "%"*n)
+            self.assertEqual(expandvars("$$"*n), "$"*n)
+
     def test_expanduser(self):
         tester('ntpath.expanduser("test")', 'test')
 
@@ -1550,7 +1564,7 @@
         self.assertTrue(os.path.exists(r"\\.\CON"))
 
     @unittest.skipIf(sys.platform != 'win32', "Fast paths are only for win32")
-    @cpython_only
+    @support.cpython_only
     def test_fast_paths_in_use(self):
         # There are fast paths of these functions implemented in posixmodule.c.
         # Confirm that they are being used, and not the Python fallbacks in
Index: 
Python-3.15.0a1/Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ 
Python-3.15.0a1/Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst
    2025-11-13 18:28:37.447873576 +0100
@@ -0,0 +1 @@
+Fix quadratic complexity in :func:`os.path.expandvars`.

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.dpaXd7/_old  2025-12-01 11:12:42.924968899 +0100
+++ /var/tmp/diff_new_pack.dpaXd7/_new  2025-12-01 11:12:42.932969237 +0100
@@ -1,6 +1,6 @@
-mtime: 1762521167
-commit: e4dc67aaa984536f8d0c1ee3adaa806f7555a8f9d5e3c9f1468cdede238269ba
+mtime: 1764427083
+commit: e52bc998396e70d02592a6fbcb3faafe0087961d29203b5bb3447d31c002b838
 url: https://src.opensuse.org/python-interpreters/python315.git
-revision: e4dc67aaa984536f8d0c1ee3adaa806f7555a8f9d5e3c9f1468cdede238269ba
+revision: e52bc998396e70d02592a6fbcb3faafe0087961d29203b5bb3447d31c002b838
 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      2025-11-29 15:38:15.000000000 +0100
@@ -0,0 +1 @@
+.osc

++++++ python315-rpmlintrc ++++++
--- /var/tmp/diff_new_pack.dpaXd7/_old  2025-12-01 11:12:43.232981928 +0100
+++ /var/tmp/diff_new_pack.dpaXd7/_new  2025-12-01 11:12:43.236982097 +0100
@@ -1,4 +1,5 @@
 addFilter("pem-certificate.*/usr/lib.*/python.*/test/*.pem")
-addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/tests/*.c")
-addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/test/*.cpp")
+addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/tests/.*.c")
+addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/test/.*.cpp")
+addFilter("python-bytecode-inconsistent-mtime.*/usr/lib64/python.*/.*.pyc")
 

Reply via email to