Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-Twisted for openSUSE:Factory 
checked in at 2026-10-14 18:34:15
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-Twisted (Old)
 and      /work/SRC/openSUSE:Factory/.python-Twisted.new.3698736 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-Twisted"

Wed Oct 14 18:34:15 2026 rev:81 rq:1384623 version:26.4.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-Twisted/python-Twisted.changes    
2026-06-16 18:29:24.673550913 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-Twisted.new.3698736/python-Twisted.changes   
    2026-10-14 18:34:41.694793324 +0200
@@ -1,0 +2,8 @@
+Tue Oct 13 12:22:23 UTC 2026 - Nico Krapp <[email protected]>
+
+- CVE-2026-106454: allowing nested or otherwise expensive regular
+  expression constructs to cause catastrophic backtracking when
+  matched against mailbox names (bsc#1284420)
+  * added CVE-2026-106454.patch
+
+-------------------------------------------------------------------

New:
----
  CVE-2026-106454.patch

----------(New B)----------
  New:  matched against mailbox names (bsc#1284420)
  * added CVE-2026-106454.patch
----------(New E)----------

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

Other differences:
------------------
++++++ python-Twisted.spec ++++++
--- /var/tmp/diff_new_pack.UmDWzd/_old  2026-10-14 18:34:42.800840159 +0200
+++ /var/tmp/diff_new_pack.UmDWzd/_new  2026-10-14 18:34:42.801840202 +0200
@@ -48,6 +48,8 @@
 Patch3:         remove-dependency-version-upper-bounds.patch
 # PATCH-FIX-UPSTREAM: https://github.com/twisted/twisted/pull/12661
 Patch4:         pycryptography-use-csr.patch
+# PATCH-FIX-UPSTREAM CVE-2026-106454.patch bsc#1284420 
gh#twisted/twisted@f570190
+Patch5:         CVE-2026-106454.patch
 BuildRequires:  %{python_module base >= 3.9}
 BuildRequires:  %{python_module hatch-fancy-pypi-readme}
 BuildRequires:  %{python_module hatchling}

++++++ CVE-2026-106454.patch ++++++
>From 73fe2b00519a43aeebdb501247222b3a04b45700 Mon Sep 17 00:00:00 2001
From: Glyph <[email protected]>
Date: Fri, 14 Aug 2026 18:46:34 -0700
Subject: [PATCH 1/7] add a type annotation since we're about to make changes

---
 src/twisted/mail/imap4.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/twisted/mail/imap4.py b/src/twisted/mail/imap4.py
index fdc53c10a46..5a9023f22af 100644
--- a/src/twisted/mail/imap4.py
+++ b/src/twisted/mail/imap4.py
@@ -4593,7 +4593,7 @@ def Not(query):
     return f"(NOT {query})"
 
 
-def wildcardToRegexp(wildcard, delim=None):
+def wildcardToRegexp(wildcard: str, delim: str | None = None) -> 
re.Pattern[str]:
     wildcard = wildcard.replace("*", "(?:.*?)")
     if delim is None:
         wildcard = wildcard.replace("%", "(?:.*?)")

>From b7eb623dd6391b14c149785c3972e4b077e05d6c Mon Sep 17 00:00:00 2001
From: Glyph <[email protected]>
Date: Fri, 14 Aug 2026 18:48:24 -0700
Subject: [PATCH 2/7] news fragment

---
 src/twisted/mail/newsfragments/12787.bugfix | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 src/twisted/mail/newsfragments/12787.bugfix

diff --git a/src/twisted/mail/newsfragments/12787.bugfix 
b/src/twisted/mail/newsfragments/12787.bugfix
new file mode 100644
index 00000000000..d8fd47ab1fd
--- /dev/null
+++ b/src/twisted/mail/newsfragments/12787.bugfix
@@ -0,0 +1 @@
+twisted.mail.imap4 no longer allows clients to specify arbitrary regexes.

>From ed5c194b78b501120c8548767cf3033e3a81d3ba Mon Sep 17 00:00:00 2001
From: Glyph <[email protected]>
Date: Fri, 14 Aug 2026 18:57:39 -0700
Subject: [PATCH 3/7] failing test that uses a python regexp

---
 src/twisted/mail/test/test_imap.py | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/src/twisted/mail/test/test_imap.py 
b/src/twisted/mail/test/test_imap.py
index 9fdc97ed735..bd7eef8b7c7 100644
--- a/src/twisted/mail/test/test_imap.py
+++ b/src/twisted/mail/test/test_imap.py
@@ -6,6 +6,7 @@
 """
 Test case for twisted.mail.imap4
 """
+
 from __future__ import annotations
 
 import base64
@@ -17,6 +18,7 @@
 from collections import OrderedDict
 from io import BytesIO
 from itertools import chain
+from typing import Any
 from unittest import skipIf
 
 from zope.interface import implementer
@@ -2554,7 +2556,7 @@ def unsubscribe():
         )
         return d
 
-    def _listSetup(self, f):
+    def _listSetup(self, f: object) -> Deferred[Any]:
         SimpleServer.theAccount.addMailbox("root/subthing")
         SimpleServer.theAccount.addMailbox("root/another-thing")
         SimpleServer.theAccount.addMailbox("non-root/subthing")
@@ -2621,6 +2623,21 @@ def lsub():
         d.addCallback(self.assertEqual, [(SimpleMailbox.flags, "/", 
"ROOT/SUBTHING")])
         return d
 
+    def test_LSubNoRegex(self) -> Deferred[None]:
+        """
+        LSUB commands should use IMAP wildcards, and not be treated as Python
+        regexes.
+        """
+        SimpleServer.theAccount.subscribe("ROOT/SUBTHING")
+
+        def lsub():
+            return self.client.lsub("root", "root/.*")
+
+        d = self._listSetup(lsub)
+        d.addCallback(self.assertListDelimiterAndMailboxAreStrings)
+        d.addCallback(self.assertEqual, [])
+        return d
+
     def testStatus(self):
         SimpleServer.theAccount.addMailbox("root/subthing")
 
@@ -7125,7 +7142,7 @@ def cbCopy(results):
         return d.addCallback(cbCopy)
 
 
-@skipIf(not ClientTLSContext, "OpenSSL not present")  # 
type:ignore[truthy-function]
+@skipIf(not ClientTLSContext, "OpenSSL not present")  # type: 
ignore[truthy-function]
 @skipIf(not interfaces.IReactorSSL(reactor, None), "Reactor doesn't support 
SSL")
 class TLSTests(IMAP4HelperMixin, TestCase):
     serverCTX = None

>From 2f8a3c29246f4eb324690e06a9767a11dc4aec9f Mon Sep 17 00:00:00 2001
From: Glyph <[email protected]>
Date: Fri, 14 Aug 2026 18:57:50 -0700
Subject: [PATCH 4/7] make it pass by escaping everything else

---
 src/twisted/mail/imap4.py | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/src/twisted/mail/imap4.py b/src/twisted/mail/imap4.py
index 5a9023f22af..52dd1553a05 100644
--- a/src/twisted/mail/imap4.py
+++ b/src/twisted/mail/imap4.py
@@ -4594,12 +4594,20 @@ def Not(query):
 
 
 def wildcardToRegexp(wildcard: str, delim: str | None = None) -> 
re.Pattern[str]:
-    wildcard = wildcard.replace("*", "(?:.*?)")
-    if delim is None:
-        wildcard = wildcard.replace("%", "(?:.*?)")
-    else:
-        wildcard = wildcard.replace("%", "(?:(?:[^%s])*?)" % re.escape(delim))
-    return re.compile(wildcard, re.I)
+    # Split on the two IMAP wildcards, escape everything else
+    parts = re.split(r"([*%])", wildcard)
+    result = []
+    for p in parts:
+        if p == "*":
+            result.append("(?:.*?)")
+        elif p == "%":
+            if delim is None:
+                result.append("(?:.*?)")
+            else:
+                result.append(f"(?:(?:[^{re.escape(delim)}])*?)")
+        else:
+            result.append(re.escape(p))
+    return re.compile("".join(result), re.I)
 
 
 def splitQuoted(s):

>From 47fca7ef2e2333b8fbdeec659c0df7934123b13e Mon Sep 17 00:00:00 2001
From: Glyph <[email protected]>
Date: Sat, 15 Aug 2026 09:04:00 -0700
Subject: [PATCH 5/7] add more potential matches to exclude to be sure

Co-authored-by: Adi Roiban <[email protected]>
---
 src/twisted/mail/test/test_imap.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/twisted/mail/test/test_imap.py 
b/src/twisted/mail/test/test_imap.py
index bd7eef8b7c7..872ab3d392c 100644
--- a/src/twisted/mail/test/test_imap.py
+++ b/src/twisted/mail/test/test_imap.py
@@ -2629,6 +2629,8 @@ def test_LSubNoRegex(self) -> Deferred[None]:
         regexes.
         """
         SimpleServer.theAccount.subscribe("ROOT/SUBTHING")
+        SimpleServer.theAccount.subscribe("ROOT/.SUBMATCH")
+        SimpleServer.theAccount.subscribe("ROOT/.DEEP/MATCH")
 
         def lsub():
             return self.client.lsub("root", "root/.*")

>From 67a34dbb8bc3f4ff387d6665e9870c3a76ae625b Mon Sep 17 00:00:00 2001
From: Glyph <[email protected]>
Date: Sat, 15 Aug 2026 09:18:24 -0700
Subject: [PATCH 6/7] per code review, docstring for wildcardToRegexp

---
 src/twisted/mail/imap4.py | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/twisted/mail/imap4.py b/src/twisted/mail/imap4.py
index 52dd1553a05..4ca0463f0c6 100644
--- a/src/twisted/mail/imap4.py
+++ b/src/twisted/mail/imap4.py
@@ -4594,7 +4594,21 @@ def Not(query):
 
 
 def wildcardToRegexp(wildcard: str, delim: str | None = None) -> 
re.Pattern[str]:
-    # Split on the two IMAP wildcards, escape everything else
+    """
+    Convert what the IMAP describes as a "mailbox name with possible wildcards"
+    into a regular expression that will match a full mailbox name.
+
+    @param wildcard: the mailbox name matching expression which may contain
+        IMAP wildcards, e.g. C{*} and C{%}.  Note that this is I{not} treated
+        as a regular expression itself, and any regex syntax will be matched
+        literally.
+
+    @param delim: the delimiter between IMAP path elements, if any.  Note that
+        this is always C{"/"} when used by Twisted's IMAP server.
+
+    @return: a L{re.Pattern} that will match IMAP mailbox names with the given
+        wildcards.
+    """
     parts = re.split(r"([*%])", wildcard)
     result = []
     for p in parts:

>From 5a020d27c67f19d7592af3f73f5f78f9cac85344 Mon Sep 17 00:00:00 2001
From: Glyph <[email protected]>
Date: Sat, 15 Aug 2026 09:37:40 -0700
Subject: [PATCH 7/7] per code review test some more cases

---
 src/twisted/mail/test/test_imap.py | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/src/twisted/mail/test/test_imap.py 
b/src/twisted/mail/test/test_imap.py
index 872ab3d392c..b4bebcce3f4 100644
--- a/src/twisted/mail/test/test_imap.py
+++ b/src/twisted/mail/test/test_imap.py
@@ -2628,16 +2628,32 @@ def test_LSubNoRegex(self) -> Deferred[None]:
         LSUB commands should use IMAP wildcards, and not be treated as Python
         regexes.
         """
+        # Subscribe to existing mailbox.
         SimpleServer.theAccount.subscribe("ROOT/SUBTHING")
-        SimpleServer.theAccount.subscribe("ROOT/.SUBMATCH")
-        SimpleServer.theAccount.subscribe("ROOT/.DEEP/MATCH")
+
+        for newMailbox in [
+            # Verify that dot is treated literally.
+            "ROOT/.SUBMATCH",
+            # And can match nested things, since we're testing *.
+            "ROOT/.DEEP/MATCH",
+            # But only at the start of the match.
+            "ROOT/DEEP/.MATCH",
+        ]:
+            SimpleServer.theAccount.addMailbox(newMailbox)
+            SimpleServer.theAccount.subscribe(newMailbox)
 
         def lsub():
             return self.client.lsub("root", "root/.*")
 
         d = self._listSetup(lsub)
         d.addCallback(self.assertListDelimiterAndMailboxAreStrings)
-        d.addCallback(self.assertEqual, [])
+        d.addCallback(
+            self.assertEqual,
+            [
+                (SimpleMailbox.flags, "/", "ROOT/.SUBMATCH"),
+                (SimpleMailbox.flags, "/", "ROOT/.DEEP/MATCH"),
+            ],
+        )
         return d
 
     def testStatus(self):

Reply via email to