Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-asgiref for openSUSE:Factory 
checked in at 2026-02-17 16:37:51
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-asgiref (Old)
 and      /work/SRC/openSUSE:Factory/.python-asgiref.new.1977 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-asgiref"

Tue Feb 17 16:37:51 2026 rev:15 rq:1333411 version:3.11.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-asgiref/python-asgiref.changes    
2025-12-11 18:32:17.678682791 +0100
+++ /work/SRC/openSUSE:Factory/.python-asgiref.new.1977/python-asgiref.changes  
2026-02-17 16:39:09.946898811 +0100
@@ -1,0 +2,7 @@
+Mon Feb 16 17:10:33 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 3.11.1 (bsc#1257403, CVE-2025-14550):
+  * Fixed sync_to_async wrapping callables with attribute named context
+  * CVE-2025-14550: Fixed duplicate header handling in WsgiToAsgi.
+
+-------------------------------------------------------------------

Old:
----
  asgiref-3.11.0.tar.gz

New:
----
  asgiref-3.11.1.tar.gz

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

Other differences:
------------------
++++++ python-asgiref.spec ++++++
--- /var/tmp/diff_new_pack.kwN4rl/_old  2026-02-17 16:39:10.826935599 +0100
+++ /var/tmp/diff_new_pack.kwN4rl/_new  2026-02-17 16:39:10.826935599 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-asgiref
 #
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-asgiref
-Version:        3.11.0
+Version:        3.11.1
 Release:        0
 Summary:        ASGI specs, helper code, and adapters
 License:        BSD-3-Clause

++++++ asgiref-3.11.0.tar.gz -> asgiref-3.11.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.11.0/PKG-INFO new/asgiref-3.11.1/PKG-INFO
--- old/asgiref-3.11.0/PKG-INFO 2025-11-19 16:32:07.114693600 +0100
+++ new/asgiref-3.11.1/PKG-INFO 2026-02-03 14:29:22.749848600 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: asgiref
-Version: 3.11.0
+Version: 3.11.1
 Summary: ASGI specs, helper code, and adapters
 Home-page: https://github.com/django/asgiref/
 Author: Django Software Foundation
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.11.0/asgiref/__init__.py 
new/asgiref-3.11.1/asgiref/__init__.py
--- old/asgiref-3.11.0/asgiref/__init__.py      2025-11-19 16:31:06.000000000 
+0100
+++ new/asgiref-3.11.1/asgiref/__init__.py      2026-02-03 14:27:52.000000000 
+0100
@@ -1 +1 @@
-__version__ = "3.11.0"
+__version__ = "3.11.1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.11.0/asgiref/sync.py 
new/asgiref-3.11.1/asgiref/sync.py
--- old/asgiref-3.11.0/asgiref/sync.py  2025-11-19 16:29:14.000000000 +0100
+++ new/asgiref-3.11.1/asgiref/sync.py  2026-01-29 14:13:52.000000000 +0100
@@ -432,9 +432,11 @@
             or iscoroutinefunction(getattr(func, "__call__", func))
         ):
             raise TypeError("sync_to_async can only be applied to sync 
functions.")
+
+        functools.update_wrapper(self, func)
         self.func = func
         self.context = context
-        functools.update_wrapper(self, func)
+
         self._thread_sensitive = thread_sensitive
         markcoroutinefunction(self)
         if thread_sensitive and executor is not None:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.11.0/asgiref/wsgi.py 
new/asgiref-3.11.1/asgiref/wsgi.py
--- old/asgiref-3.11.0/asgiref/wsgi.py  2025-10-05 11:02:07.000000000 +0200
+++ new/asgiref-3.11.1/asgiref/wsgi.py  2026-02-03 14:27:10.000000000 +0100
@@ -1,4 +1,5 @@
 import sys
+from collections import defaultdict
 from tempfile import SpooledTemporaryFile
 
 from asgiref.sync import AsyncToSync, sync_to_async
@@ -9,8 +10,9 @@
     Wraps a WSGI application to make it into an ASGI application.
     """
 
-    def __init__(self, wsgi_application):
+    def __init__(self, wsgi_application, duplicate_header_limit=100):
         self.wsgi_application = wsgi_application
+        self.duplicate_header_limit = duplicate_header_limit
 
     async def __call__(self, scope, receive, send):
         """
@@ -18,7 +20,9 @@
         We return a new WsgiToAsgiInstance here with the WSGI app
         and the scope, ready to respond when it is __call__ed.
         """
-        await WsgiToAsgiInstance(self.wsgi_application)(scope, receive, send)
+        await WsgiToAsgiInstance(self.wsgi_application, 
self.duplicate_header_limit)(
+            scope, receive, send
+        )
 
 
 class WsgiToAsgiInstance:
@@ -26,8 +30,9 @@
     Per-socket instance of a wrapped WSGI application
     """
 
-    def __init__(self, wsgi_application):
+    def __init__(self, wsgi_application, duplicate_header_limit=100):
         self.wsgi_application = wsgi_application
+        self.duplicate_header_limit = duplicate_header_limit
         self.response_started = False
         self.response_content_length = None
 
@@ -84,6 +89,7 @@
             environ["REMOTE_ADDR"] = scope["client"][0]
 
         # Go through headers and make them into environ entries
+        _headers = defaultdict(list)
         for name, value in self.scope.get("headers", []):
             name = name.decode("latin1")
             if name == "content-length":
@@ -94,9 +100,17 @@
                 corrected_name = "HTTP_%s" % name.upper().replace("-", "_")
             # HTTPbis say only ASCII chars are allowed in headers, but we 
latin1 just in case
             value = value.decode("latin1")
-            if corrected_name in environ:
-                value = environ[corrected_name] + "," + value
-            environ[corrected_name] = value
+            if (
+                self.duplicate_header_limit
+                and len(_headers[corrected_name]) >= 
self.duplicate_header_limit
+            ):
+                raise ValueError(
+                    f"Too many duplicate headers: {corrected_name} exceeds 
limit of"
+                    f"{self.duplicate_header_limit}"
+                )
+            _headers[corrected_name].append(value)
+        for name, values in _headers.items():
+            environ[name] = ",".join(values)
         return environ
 
     def start_response(self, status, response_headers, exc_info=None):
@@ -138,7 +152,24 @@
         this so that the start_response callable is called in the same thread.
         """
         # Translate the scope and incoming request body into a WSGI environ
-        environ = self.build_environ(self.scope, body)
+        try:
+            environ = self.build_environ(self.scope, body)
+        except ValueError:
+            # Return 400 Bad Request if header limit exceeded
+            self.sync_send(
+                {
+                    "type": "http.response.start",
+                    "status": 400,
+                    "headers": [(b"content-type", b"text/plain")],
+                }
+            )
+            self.sync_send(
+                {
+                    "type": "http.response.body",
+                    "body": b"Bad Request: Too many duplicate headers",
+                }
+            )
+            return
         # Run the WSGI app
         bytes_sent = 0
         for output in self.wsgi_application(environ, self.start_response):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.11.0/asgiref.egg-info/PKG-INFO 
new/asgiref-3.11.1/asgiref.egg-info/PKG-INFO
--- old/asgiref-3.11.0/asgiref.egg-info/PKG-INFO        2025-11-19 
16:32:07.000000000 +0100
+++ new/asgiref-3.11.1/asgiref.egg-info/PKG-INFO        2026-02-03 
14:29:22.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: asgiref
-Version: 3.11.0
+Version: 3.11.1
 Summary: ASGI specs, helper code, and adapters
 Home-page: https://github.com/django/asgiref/
 Author: Django Software Foundation
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.11.0/tests/test_sync_contextvars.py 
new/asgiref-3.11.1/tests/test_sync_contextvars.py
--- old/asgiref-3.11.0/tests/test_sync_contextvars.py   2025-11-19 
16:29:14.000000000 +0100
+++ new/asgiref-3.11.1/tests/test_sync_contextvars.py   2026-01-29 
14:13:52.000000000 +0100
@@ -138,3 +138,24 @@
     sync_function = async_to_sync(async_function)
     assert sync_function() == 42
     assert foo.get() == "baz"
+
+
[email protected]
+async def 
test_sync_to_async_contextvars_with_callable_with_context_attribute():
+    """
+    Tests that a callable object with a `context` attribute
+    can be wrapped with `sync_to_async` without overwriting the `context` 
attribute
+    and still returns the expected result.
+    """
+    # Define sync Callable
+    class SyncCallable:
+        def __init__(self):
+            # Should not be copied to the SyncToAsync wrapper.
+            self.context = ...
+
+        def __call__(self):
+            return 42
+
+    async_function = sync_to_async(SyncCallable())
+    assert async_function.context is None
+    assert await async_function() == 42
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.11.0/tests/test_wsgi.py 
new/asgiref-3.11.1/tests/test_wsgi.py
--- old/asgiref-3.11.0/tests/test_wsgi.py       2024-12-07 09:15:18.000000000 
+0100
+++ new/asgiref-3.11.1/tests/test_wsgi.py       2026-02-03 14:27:10.000000000 
+0100
@@ -315,3 +315,68 @@
     }
 
     assert (await instance.receive_output(1)) == {"type": "http.response.body"}
+
+
[email protected]
+async def test_duplicate_header_limit():
+    def wsgi_application(environ, start_response):
+        start_response("200 OK", [])
+        return [b"OK"]
+
+    application = WsgiToAsgi(wsgi_application, duplicate_header_limit=5)
+    instance = ApplicationCommunicator(
+        application,
+        {
+            "type": "http",
+            "http_version": "1.0",
+            "method": "GET",
+            "path": "/",
+            "query_string": b"",
+            "headers": [[b"x-test", b"value"] for _ in range(10)],
+        },
+    )
+    await instance.send_input({"type": "http.request"})
+
+    assert (await instance.receive_output(1)) == {
+        "type": "http.response.start",
+        "status": 400,
+        "headers": [(b"content-type", b"text/plain")],
+    }
+    assert (await instance.receive_output(1)) == {
+        "type": "http.response.body",
+        "body": b"Bad Request: Too many duplicate headers",
+    }
+
+
[email protected]
+async def test_duplicate_header_limit_disabled():
+    def wsgi_application(environ, start_response):
+        assert "HTTP_X_TEST" in environ
+        start_response("200 OK", [])
+        return [b"OK"]
+
+    application = WsgiToAsgi(wsgi_application, duplicate_header_limit=None)
+    instance = ApplicationCommunicator(
+        application,
+        {
+            "type": "http",
+            "http_version": "1.0",
+            "method": "GET",
+            "path": "/",
+            "query_string": b"",
+            "headers": [[b"x-test", b"value"] for _ in range(200)],
+        },
+    )
+    await instance.send_input({"type": "http.request"})
+
+    assert (await instance.receive_output(1)) == {
+        "type": "http.response.start",
+        "status": 200,
+        "headers": [],
+    }
+    assert (await instance.receive_output(1)) == {
+        "type": "http.response.body",
+        "body": b"OK",
+        "more_body": True,
+    }
+    assert (await instance.receive_output(1)) == {"type": "http.response.body"}

Reply via email to