Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package libsoup2 for openSUSE:Factory 
checked in at 2026-10-08 15:12:47
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libsoup2 (Old)
 and      /work/SRC/openSUSE:Factory/.libsoup2.new.3698736 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libsoup2"

Thu Oct  8 15:12:47 2026 rev:28 rq:1382958 version:2.74.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/libsoup2/libsoup2.changes        2026-09-23 
14:33:06.452714103 +0200
+++ /work/SRC/openSUSE:Factory/.libsoup2.new.3698736/libsoup2.changes   
2026-10-08 15:12:54.985568735 +0200
@@ -1,0 +2,34 @@
+Tue Oct  6 19:55:28 UTC 2026 - Michael Gorse <[email protected]>
+
+- CVE-2026-15711: WebSocket denial of service via oversized control
+  frame protocol violation (bsc#1271446)
+  * libsoup2-CVE-2026-15711.patch
+- CVE-2026-15714: Out-of-bounds read in
+  soup_multipart_input_stream_read_headers via an oversized
+  multipart boundary string (bsc#1271449)
+  * libsoup2-CVE-2026-15714.patch
+- CVE-2026-66339: proxy credentials leak to destination server via
+  `Proxy-Authorization` header in CONNECT tunnels (bsc#1273156)
+  * libsoup2-CVE-2026-66339.patch
+- CVE-2026-12547: Header "Proxy-Authorization" leads to credentials
+  being send to different proxy after proxy switch (bsc#1272195)
+  * libsoup2-CVE-2026-12547.patch
+- CVE-2026-102558: Heap buffer overflow during WebSocket
+  receive-buffer growth (bsc#1283302)
+  * libsoup2-CVE-2026-102558.patch
+- CVE-2026-102556: Heap buffer overflow from WebSocket Pong signal
+  type confusion (bsc#1283300)
+  * libsoup2-CVE-2026-102556.patch
+- CVE-2026-102560: Heap buffer overflow during outgoing
+  permessage-deflate buffer growth (bsc#1283304)
+  * libsoup2-CVE-2026-102560.patch
+- CVE-2026-102557: Heap buffer overflow during WebSocket message
+  reassembly (bsc#1283301)
+- CVE-2026-102559: Heap buffer overflow during WebSocket
+  client-frame masking (bsc#1283303)
+  * libsoup2-CVE-2026-102557.patch
+- Tweak libsoup2-CVE-2026-12548.patch and
+  libsoup2-CVE-2026-2708.patch to remove superfluous initializer
+  values in the tests.
+
+-------------------------------------------------------------------
@@ -41 +75 @@
-  (bsc#1256418 CVE-2026-0716 glgo#GNOME/libsoup#476).
+  (bsc#1271401 CVE-2026-12478 glgo#GNOME/libsoup#476).

New:
----
  libsoup2-CVE-2026-102556.patch
  libsoup2-CVE-2026-102557.patch
  libsoup2-CVE-2026-102558.patch
  libsoup2-CVE-2026-102560.patch
  libsoup2-CVE-2026-12547.patch
  libsoup2-CVE-2026-15711.patch
  libsoup2-CVE-2026-15714.patch
  libsoup2-CVE-2026-66339.patch

----------(New B)----------
  New:  type confusion (bsc#1283300)
  * libsoup2-CVE-2026-102556.patch
- CVE-2026-102560: Heap buffer overflow during outgoing
  New:  client-frame masking (bsc#1283303)
  * libsoup2-CVE-2026-102557.patch
- Tweak libsoup2-CVE-2026-12548.patch and
  New:  receive-buffer growth (bsc#1283302)
  * libsoup2-CVE-2026-102558.patch
- CVE-2026-102556: Heap buffer overflow from WebSocket Pong signal
  New:  permessage-deflate buffer growth (bsc#1283304)
  * libsoup2-CVE-2026-102560.patch
- CVE-2026-102557: Heap buffer overflow during WebSocket message
  New:  being send to different proxy after proxy switch (bsc#1272195)
  * libsoup2-CVE-2026-12547.patch
- CVE-2026-102558: Heap buffer overflow during WebSocket
  New:  frame protocol violation (bsc#1271446)
  * libsoup2-CVE-2026-15711.patch
- CVE-2026-15714: Out-of-bounds read in
  New:  multipart boundary string (bsc#1271449)
  * libsoup2-CVE-2026-15714.patch
- CVE-2026-66339: proxy credentials leak to destination server via
  New:  `Proxy-Authorization` header in CONNECT tunnels (bsc#1273156)
  * libsoup2-CVE-2026-66339.patch
- CVE-2026-12547: Header "Proxy-Authorization" leads to credentials
----------(New E)----------

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

Other differences:
------------------
++++++ libsoup2.spec ++++++
--- /var/tmp/diff_new_pack.pFOzxP/_old  2026-10-08 15:12:57.610678741 +0200
+++ /var/tmp/diff_new_pack.pFOzxP/_new  2026-10-08 15:12:57.612678825 +0200
@@ -110,8 +110,24 @@
 Patch41:        libsoup2-CVE-2026-1801.patch
 # PATCH-FIX-UPSTREAM libsoup2-CVE-2026-12548.patch bsc#1272196, 
glgo#GNOME/libsoup!524 [email protected] -- Fix heap out-of-bounds read flaw 
when parsing multipart HTTP messages
 Patch42:        libsoup2-CVE-2026-12548.patch
-# PATCH-FIX-UPSTREAM libsoup2-CVE-2026-77680.patch bsc#1276759, bsc#1275988, 
glgo#GNOME/libsoup!550 [email protected] -- Fix flaw in HTTP Range heaader 
processing
+# PATCH-FIX-UPSTREAM libsoup2-CVE-2026-77680.patch bsc#1276759, bsc#1275988, 
glgo#GNOME/libsoup!550 [email protected] -- Fix flaw in HTTP Range header 
processing
 Patch43:        libsoup2-CVE-2026-77680.patch
+# PATCH-FIX-UPSTREAM libsoup2-CVE-2026-15711.patch bsc#1271446 [email protected] 
-- websocket: Reject oversized control frames.
+Patch44:        libsoup2-CVE-2026-15711.patch
+# PATCH-FIX-UPSTREAM libsoup2-CVE-2026-15714.patch bsc#1271449 [email protected] 
-- multipart: Fix out-of-bounds read in case of an excessively.
+Patch45:        libsoup2-CVE-2026-15714.patch
+# PATCH-FIX-UPSTREAM libsoup2-CVE-2026-66339.patch bsc#1273156 [email protected] 
-- auth: Don't send Proxy-Authorization through an established.
+Patch46:        libsoup2-CVE-2026-66339.patch
+# PATCH-FIX-UPSTREAM libsoup2-CVE-2026-12547.patch bsc#1272195 [email protected] 
-- auth: Scope cached proxy credentials to the proxy they came.
+Patch47:        libsoup2-CVE-2026-12547.patch
+# PATCH-FIX-UPSTREAM libsoup2-CVE-2026-102558.patch bsc#1283302 
[email protected] -- websocket: Cap incoming frame payloads at what the buffer 
can hold.
+Patch48:        libsoup2-CVE-2026-102558.patch
+# PATCH-FIX-UPSTREAM libsoup2-CVE-2026-102556.patch bsc#1283300 
[email protected] -- websocket: Emit the pong signal with a GBytes as declared.
+Patch49:        libsoup2-CVE-2026-102556.patch
+# PATCH-FIX-UPSTREAM libsoup2-CVE-2026-102560.patch bsc#1283304 
[email protected] -- websocket: Bound outgoing permessage-deflate buffer growth.
+Patch50:        libsoup2-CVE-2026-102560.patch
+# PATCH-FIX-UPSTREAM libsoup2-CVE-2026-102557.patch bsc#1283301 bsc#1283303 
[email protected] -- websocket: Cap message reassembly and outgoing frames at the 
buffer limit.
+Patch51:        libsoup2-CVE-2026-102557.patch
 
 BuildRequires:  glib-networking
 BuildRequires:  meson >= 0.50

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.pFOzxP/_old  2026-10-08 15:12:57.678681591 +0200
+++ /var/tmp/diff_new_pack.pFOzxP/_new  2026-10-08 15:12:57.681681717 +0200
@@ -1,7 +1,7 @@
-mtime: 1789997740
-commit: 987683895276916fd7ff1ac2f6f74d00e739b13313153be3eaff48c5dd08508e
+mtime: 1791317579
+commit: a5c48ee5831de2c24c1bb6b38d0f95fdac1261e3f937831a16628a969b3d8149
 url: https://src.opensuse.org/GNOME/libsoup2
-revision: 987683895276916fd7ff1ac2f6f74d00e739b13313153be3eaff48c5dd08508e
+revision: a5c48ee5831de2c24c1bb6b38d0f95fdac1261e3f937831a16628a969b3d8149
 trackingbranch: factory
 projectscmsync: https://src.opensuse.org/GNOME/_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-10-06 22:12:59.000000000 +0200
@@ -0,0 +1,4 @@
+*.obscpio
+*.osc
+_build.*
+.pbuild

++++++ libsoup2-CVE-2026-102556.patch ++++++
>From d2cbaf25f81a9ce041714cf842c366c67b0b873e Mon Sep 17 00:00:00 2001
From: Patrick Griffis <[email protected]>
Date: Fri, 4 Sep 2026 14:28:14 -0500
Subject: [PATCH] websocket: Emit the pong signal with a GBytes as declared

Fixes #554
---
 libsoup/websocket/soup-websocket-connection.c | 7 ++++---
 tests/websocket-test.c                        | 7 +++++++
 2 files changed, 11 insertions(+), 3 deletions(-)

diff -urp libsoup-2.74.3.orig/libsoup/soup-websocket-connection.c 
libsoup-2.74.3/libsoup/soup-websocket-connection.c
--- libsoup-2.74.3.orig/libsoup/soup-websocket-connection.c     2026-10-05 
13:35:09.601629971 -0500
+++ libsoup-2.74.3/libsoup/soup-websocket-connection.c  2026-10-05 
13:35:46.680248564 -0500
@@ -830,6 +830,7 @@ receive_pong (SoupWebsocketConnection *s
                       gsize len)
 {
        GByteArray *bytes;
+       GBytes *payload;
 
        g_debug ("received pong message");
 
@@ -840,9 +841,9 @@ receive_pong (SoupWebsocketConnection *s
        /* But don't include the null terminator in the byte count */
        bytes->len--;
 
-       g_signal_emit (self, signals[PONG], 0, bytes);
-       g_byte_array_unref (bytes);
-
+       payload = g_byte_array_free_to_bytes (bytes);
+       g_signal_emit (self, signals[PONG], 0, payload);
+       g_bytes_unref (payload);
 }
 
 static void

++++++ libsoup2-CVE-2026-102557.patch ++++++
>From d7f074f8c8c90216f2476522836287c6f80fc53e Mon Sep 17 00:00:00 2001
From: Patrick Griffis <[email protected]>
Date: Fri, 4 Sep 2026 14:34:38 -0500
Subject: [PATCH] websocket: Cap message reassembly and outgoing frames at the
 buffer limit

Both a reassembled message and an outgoing frame are built in GByteArrays,
whose length is a guint. A message grown past G_MAXINT by a run of
fragments (or by an extension that expands its input) and a send payload
at or above 2^32 both truncated the size passed to g_byte_array_set_size()
/ g_byte_array_sized_new() while the surrounding code kept using the full
length, corrupting the heap.

Reject a reassembled message that would exceed what the array can hold
before appending, independently of max-total-message-size, and reject an
outgoing payload above the same ceiling both before and after extensions
have processed it.

Fixes #554
---
 libsoup/websocket/soup-websocket-connection.c |  54 +++++++
 po/POTFILES.in                                |   1 +
 tests/websocket-test.c                        | 141 ++++++++++++++++++
 3 files changed, 196 insertions(+)

Index: libsoup-2.74.3/libsoup/soup-websocket-connection.c
===================================================================
--- libsoup-2.74.3.orig/libsoup/soup-websocket-connection.c
+++ libsoup-2.74.3/libsoup/soup-websocket-connection.c
@@ -22,6 +22,8 @@
 
 #include <string.h>
 
+#include <glib/gi18n-lib.h>
+
 #include "soup-websocket-connection.h"
 #include "soup-enum-types.h"
 #include "soup-io-stream.h"
@@ -162,6 +164,16 @@ struct _SoupWebsocketConnectionPrivate {
  */
 #define MAX_INCOMING_PAYLOAD_SIZE ((guint64)G_MAXINT - MAX_FRAME_HEADER_SIZE - 
READ_BUFFER_SIZE)
 
+/* Likewise for a reassembled message and for an outgoing frame, which are
+ * also built in GByteArrays. Extensions may expand a frame's payload, so the
+ * incoming cap above does not bound these on its own.
+ */
+#define MAX_MESSAGE_SIZE ((guint64)G_MAXINT - 1)
+#define MAX_OUTGOING_PAYLOAD_SIZE ((gsize)G_MAXINT - MAX_FRAME_HEADER_SIZE)
+
+static void too_big_outgoing_payload_error_and_close (SoupWebsocketConnection 
*self,
+                                                      gsize                    
len);
+
 G_DEFINE_TYPE_WITH_PRIVATE (SoupWebsocketConnection, 
soup_websocket_connection, G_TYPE_OBJECT)
 
 static void queue_frame (SoupWebsocketConnection *self, 
SoupWebsocketQueueFlags flags,
@@ -503,6 +515,11 @@ send_message (SoupWebsocketConnection *s
                return;
        }
 
+       if (length > MAX_OUTGOING_PAYLOAD_SIZE) {
+               too_big_outgoing_payload_error_and_close (self, length);
+               return;
+       }
+
        bytes = g_byte_array_sized_new (14 + length);
        outer = bytes->data;
        outer[0] = 0x80 | opcode;
@@ -523,6 +540,14 @@ send_message (SoupWebsocketConnection *s
        data = g_bytes_get_data (filtered_bytes, &length);
        buffered_amount = length;
 
+       /* Extensions may have grown the payload */
+       if (length > MAX_OUTGOING_PAYLOAD_SIZE) {
+               g_byte_array_free (bytes, TRUE);
+               g_bytes_unref (filtered_bytes);
+               too_big_outgoing_payload_error_and_close (self, length);
+               return;
+       }
+
        /* If control message, check payload size */
        if (opcode & 0x08) {
                if (length > 125) {
@@ -703,6 +728,20 @@ too_big_error_and_close (SoupWebsocketCo
 }
 
 static void
+too_big_outgoing_payload_error_and_close (SoupWebsocketConnection *self,
+                                          gsize len)
+{
+       GError *error;
+
+       error = g_error_new_literal (SOUP_WEBSOCKET_ERROR,
+                                    SOUP_WEBSOCKET_CLOSE_TOO_BIG,
+                                    _("WebSocket message is too large to 
send"));
+       g_debug ("attempted to send a frame of size %" G_GSIZE_FORMAT ", but 
max supported size is %" G_GSIZE_FORMAT,
+                len, MAX_OUTGOING_PAYLOAD_SIZE);
+       emit_error_and_close (self, error, FALSE);
+}
+
+static void
 close_connection (SoupWebsocketConnection *self,
                   gushort                  code,
                   const char              *data)
@@ -928,6 +967,21 @@ process_contents (SoupWebsocketConnectio
                        g_debug ("received frame %d with %d payload", 
(int)opcode, (int)payload_len);
                }
 
+               /* Regardless of max-total-message-size, the reassembled message
+                * must fit in a GByteArray. Check before allocating, since
+                * g_byte_array_sized_new() takes a guint too.
+                */
+               if (payload_len > MAX_MESSAGE_SIZE - (pv->message_data ? 
pv->message_data->len : 0)) {
+                       guint64 message_size = pv->message_data ? 
pv->message_data->len : 0;
+
+                       if (payload_len > G_MAXUINT64 - message_size)
+                               message_size = G_MAXUINT64;
+                       else
+                               message_size += payload_len;
+                       too_big_error_and_close (self, message_size);
+                       return;
+               }
+
                if (opcode) {
                        pv->message_opcode = opcode;
                        pv->message_data = g_byte_array_sized_new (payload_len 
+ 1);
Index: libsoup-2.74.3/po/POTFILES.in
===================================================================
--- libsoup-2.74.3.orig/po/POTFILES.in
+++ libsoup-2.74.3/po/POTFILES.in
@@ -10,4 +10,5 @@ libsoup/soup-server.c
 libsoup/soup-session.c
 libsoup/soup-socket.c
 libsoup/soup-websocket.c
+libsoup/soup-websocket-connection.c
 libsoup/soup-tld.c
Index: libsoup-2.74.3/tests/websocket-test.c
===================================================================
--- libsoup-2.74.3.orig/tests/websocket-test.c
+++ libsoup-2.74.3/tests/websocket-test.c
@@ -21,6 +21,9 @@
 #include "test-utils.h"
 
 #include <zlib.h>
+#ifdef G_OS_UNIX
+#include <sys/mman.h>
+#endif
 
 typedef struct {
        GSocket *listener;
@@ -38,6 +41,7 @@ typedef struct {
        GIOStream *raw_server;
 
        gboolean enable_extensions;
+       GType server_extension_type;
        gboolean disable_deflate_in_message;
 
        GList *initial_cookies;
@@ -163,6 +167,8 @@ got_connection (GSocket *listener,
                                                                           
NULL, NULL));
                        extensions = g_list_prepend (extensions, extension);
                }
+               if (test->server_extension_type)
+                       extensions = g_list_prepend (extensions, g_object_new 
(test->server_extension_type, NULL));
                test->server = soup_websocket_connection_new_with_extensions 
(G_IO_STREAM (conn), uri,
                                                                              
SOUP_WEBSOCKET_CONNECTION_SERVER,
                                                                              
NULL, NULL,
@@ -2080,6 +2086,50 @@ test_bad_length_unmasked (Test *test,
        WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == 
SOUP_WEBSOCKET_STATE_CLOSED);
 }
 
+/* Sending a payload larger than a GByteArray can hold. The frame is built in
+ * a GByteArray, so g_byte_array_sized_new() and g_byte_array_append() used to
+ * truncate the length while xor_with_mask() still iterated the full length,
+ * writing past the small allocation. The mapping is never read when the size
+ * is rejected up front, so MAP_NORESERVE keeps this cheap.
+ */
+static void
+test_send_over_buffer_limit (Test *test,
+                             gconstpointer unused)
+{
+#ifdef G_OS_UNIX
+       GError *error = NULL;
+       gsize length = (gsize)G_MAXINT + 1;
+       void *payload;
+
+       if (sizeof (gsize) < 8) {
+               g_test_skip ("needs a 64-bit gsize");
+               return;
+       }
+
+       payload = mmap (NULL, length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | 
MAP_NORESERVE, -1, 0);
+       if (payload == MAP_FAILED) {
+               g_test_skip ("could not map a 2 GiB region");
+               return;
+       }
+
+       g_signal_handlers_disconnect_by_func (test->client, 
on_error_not_reached, NULL);
+       g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), 
&error);
+
+       soup_websocket_connection_send_binary (test->client, payload, length);
+
+       wait_for_websocket_error (&error);
+       g_assert_error (error, SOUP_WEBSOCKET_ERROR, 
SOUP_WEBSOCKET_CLOSE_TOO_BIG);
+       g_clear_error (&error);
+
+       WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == 
SOUP_WEBSOCKET_STATE_CLOSED);
+       g_assert_cmpuint (soup_websocket_connection_get_close_code 
(test->client), ==, SOUP_WEBSOCKET_CLOSE_TOO_BIG);
+
+       munmap (payload, length);
+#else
+       g_test_skip ("needs mmap");
+#endif
+}
+
 /* A declared payload length that fits in a gsize,
  * but that the GByteArray-backed incoming buffer can never
  * hold. With no configured max-incoming-payload-size the receiver used to
@@ -2383,6 +2433,10 @@ main (int argc,
                    setup_direct_connection,
                    test_payload_over_buffer_limit,
                    teardown_direct_connection);
+       g_test_add ("/websocket/direct/send-over-buffer-limit", Test, NULL,
+                   setup_direct_connection,
+                   test_send_over_buffer_limit,
+                   teardown_direct_connection);
        g_test_add ("/websocket/soup/bad-length-masked", Test, NULL,
                    setup_soup_connection,
                    test_bad_length_masked,

++++++ libsoup2-CVE-2026-102558.patch ++++++
>From 381a474ff437d0f7573d2bb3e8d3027260ab584f Mon Sep 17 00:00:00 2001
From: Patrick Griffis <[email protected]>
Date: Fri, 4 Sep 2026 14:26:44 -0500
Subject: [PATCH] websocket: Cap incoming frame payloads at what the buffer can
 hold

Incoming frames are accumulated in a GByteArray, whose length is a guint
and whose growth is not safe beyond G_MAXINT on all supported GLib
versions. With max-incoming-payload-size set to 0 (unlimited) a peer
could declare a payload of a few GiB and stream it, and the buffer
length would eventually wrap in g_byte_array_set_size() while the read
still targeted the original offset, writing past the allocation.

Reject any payload length above what the buffer can represent as soon as
the frame header is parsed, before waiting for the payload. Doing this
check first also means the at + payload_len arithmetic can no longer
overflow, so the separate G_MAXSIZE guard that previously ran after that
addition is no longer needed.

Such frames are now reported as SOUP_WEBSOCKET_CLOSE_TOO_BIG rather than
SOUP_WEBSOCKET_CLOSE_BAD_DATA, and, as for exceeding the configured
limit, the stream is closed without a close handshake so that no further
data is buffered.

Fixes #553
Fixes #554 (bug 6)
---
Index: libsoup-2.74.3/libsoup/soup-websocket-connection.c
===================================================================
--- libsoup-2.74.3.orig/libsoup/soup-websocket-connection.c
+++ libsoup-2.74.3/libsoup/soup-websocket-connection.c
@@ -154,6 +154,13 @@ struct _SoupWebsocketConnectionPrivate {
 #define MAX_INCOMING_PAYLOAD_SIZE_DEFAULT   128 * 1024
 #define READ_BUFFER_SIZE 1024
 #define MASK_LENGTH 4
+#define MAX_FRAME_HEADER_SIZE (10 + MASK_LENGTH)
+
+/* Incoming frames are buffered in a GByteArray, which uses guint lengths and
+ * whose growth is not safe beyond G_MAXINT on all supported GLib versions.
+ * Leave room for the largest possible frame header and one extra read.
+ */
+#define MAX_INCOMING_PAYLOAD_SIZE ((guint64)G_MAXINT - MAX_FRAME_HEADER_SIZE - 
READ_BUFFER_SIZE)
 
 G_DEFINE_TYPE_WITH_PRIVATE (SoupWebsocketConnection, 
soup_websocket_connection, G_TYPE_OBJECT)
 
@@ -1074,9 +1081,14 @@ process_frame (SoupWebsocketConnection *
                break;
        }
 
-       /* Safety valve */
-       if (self->pv->max_incoming_payload_size > 0 &&
-           payload_len >= self->pv->max_incoming_payload_size) {
+       /* Safety valve. Even with no configured limit, the frame can never
+        * exceed what the incoming buffer can hold. Checking this before any
+        * arithmetic on payload_len also guarantees at + payload_len cannot
+        * overflow below.
+        */
+       if (payload_len > MAX_INCOMING_PAYLOAD_SIZE ||
+           (self->pv->max_incoming_payload_size > 0 &&
+            payload_len > self->pv->max_incoming_payload_size)) {
                too_big_error_and_close (self, payload_len);
                return FALSE;
        }
@@ -1086,12 +1098,6 @@ process_frame (SoupWebsocketConnection *
 
        payload = header + at;
 
-       /* at has a maximum value of 10 + 4 = 14 */
-       if (payload_len > G_MAXSIZE - 14) {
-               bad_data_error_and_close (self);
-               return FALSE;
-       }
-
        if (masked) {
                mask = header + at;
                payload += 4;
Index: libsoup-2.74.3/tests/websocket-test.c
===================================================================
--- libsoup-2.74.3.orig/tests/websocket-test.c
+++ libsoup-2.74.3/tests/websocket-test.c
@@ -1983,13 +1983,12 @@ test_bad_length_masked (Test *test,
        g_assert_cmpuint (written, ==, 14);
 
        WAIT_UNTIL (error != NULL);
-       g_assert_error (error, SOUP_WEBSOCKET_ERROR, 
SOUP_WEBSOCKET_CLOSE_BAD_DATA);
+       g_assert_error (error, SOUP_WEBSOCKET_ERROR, 
SOUP_WEBSOCKET_CLOSE_TOO_BIG);
        g_clear_error (&error);
 
+       /* The receiver closes the stream immediately rather than negotiating a 
close */
        WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == 
SOUP_WEBSOCKET_STATE_CLOSED);
        g_assert_true (close_event);
-
-       g_assert_cmpuint (soup_websocket_connection_get_close_code 
(test->client), ==, SOUP_WEBSOCKET_CLOSE_BAD_DATA);
 }
 
 static gpointer
@@ -2027,7 +2026,7 @@ test_bad_length_unmasked (Test *test,
        thread = g_thread_new ("send-bad-length-frame-thread", 
send_bad_length_frame_server_thread, test);
 
        WAIT_UNTIL (error != NULL || received != NULL);
-       g_assert_error (error, SOUP_WEBSOCKET_ERROR, 
SOUP_WEBSOCKET_CLOSE_BAD_DATA);
+       g_assert_error (error, SOUP_WEBSOCKET_ERROR, 
SOUP_WEBSOCKET_CLOSE_TOO_BIG);
        g_clear_error (&error);
        g_assert_null (received);
 
@@ -2039,6 +2038,44 @@ test_bad_length_unmasked (Test *test,
        WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == 
SOUP_WEBSOCKET_STATE_CLOSED);
 }
 
+/* A declared payload length that fits in a gsize,
+ * but that the GByteArray-backed incoming buffer can never
+ * hold. With no configured max-incoming-payload-size the receiver used to
+ * keep buffering until the array length wrapped.
+ */
+static void
+test_payload_over_buffer_limit (Test *test,
+                                gconstpointer unused)
+{
+       GError *error = NULL;
+       GIOStream *io;
+       gsize written;
+       /* Masked binary frame declaring a 2 GiB payload (10-byte header + 
4-byte mask) */
+       const char frame[] = 
"\x82\xff\x00\x00\x00\x00\x80\x00\x00\x00\xaa\xbb\xcc\xdd";
+       gboolean close_event = FALSE;
+
+       g_signal_handlers_disconnect_by_func (test->server, 
on_error_not_reached, NULL);
+       g_signal_connect (test->server, "error", G_CALLBACK (on_error_copy), 
&error);
+       g_signal_connect (test->client, "closed", G_CALLBACK 
(on_close_set_flag), &close_event);
+
+       soup_websocket_connection_set_max_incoming_payload_size (test->server, 
0);
+
+       io = soup_websocket_connection_get_io_stream (test->client);
+       g_output_stream_write_all (g_io_stream_get_output_stream (io),
+                                  frame, sizeof (frame) - 1, &written, NULL, 
&error);
+       g_assert_no_error (error);
+       g_assert_cmpuint (written, ==, sizeof (frame) - 1);
+
+       /* The error must be reported from the header alone, without waiting 
for payload */
+       wait_for_websocket_error (&error);
+       g_assert_error (error, SOUP_WEBSOCKET_ERROR, 
SOUP_WEBSOCKET_CLOSE_TOO_BIG);
+       g_clear_error (&error);
+
+       /* The receiver closes the stream immediately rather than negotiating a 
close */
+       WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == 
SOUP_WEBSOCKET_STATE_CLOSED);
+       g_assert_true (close_event);
+}
+
 int
 main (int argc,
       char *argv[])
@@ -2297,6 +2334,10 @@ main (int argc,
                    setup_direct_connection,
                    test_bad_length_masked,
                    teardown_direct_connection);
+       g_test_add ("/websocket/direct/payload-over-buffer-limit", Test, NULL,
+                   setup_direct_connection,
+                   test_payload_over_buffer_limit,
+                   teardown_direct_connection);
        g_test_add ("/websocket/soup/bad-length-masked", Test, NULL,
                    setup_soup_connection,
                    test_bad_length_masked,

++++++ libsoup2-CVE-2026-102560.patch ++++++
>From dbd0417ea3a569135e9a9344b7812076b7b178f1 Mon Sep 17 00:00:00 2001
From: Patrick Griffis <[email protected]>
Date: Fri, 4 Sep 2026 14:30:58 -0500
Subject: [PATCH] websocket: Bound outgoing permessage-deflate buffer growth

The outgoing deflater stored deflateBound()'s uLong result in a guint and
grew its GByteArray by that amount. For a payload approaching 4 GiB the
bound exceeds G_MAXUINT, the truncated value let buffer->len + max_length
wrap on a later growth, and zlib then wrote through next_out past the
allocation.

Refuse payloads whose compressed bound cannot fit in a GByteArray before
compressing, and check each incremental growth as well.

Fixes #554
---
 .../soup-websocket-extension-deflate.c        | 24 +++++++++-
 tests/websocket-test.c                        | 44 +++++++++++++++++++
 2 files changed, 67 insertions(+), 1 deletion(-)

diff -urp libsoup-2.74.3.orig/libsoup/soup-websocket-extension-deflate.c 
libsoup-2.74.3/libsoup/soup-websocket-extension-deflate.c
--- libsoup-2.74.3.orig/libsoup/soup-websocket-extension-deflate.c      
2022-10-11 13:27:22.000000000 -0500
+++ libsoup-2.74.3/libsoup/soup-websocket-extension-deflate.c   2026-10-05 
13:37:27.434343591 -0500
@@ -292,6 +292,7 @@ soup_websocket_extension_deflate_process
 {
         const guint8 *payload_data;
         gsize payload_length;
+        unsigned long bound;
         guint max_length;
         gboolean control;
         GByteArray *buffer;
@@ -315,11 +316,21 @@ soup_websocket_extension_deflate_process
         if (payload_length == 0)
                 return payload;
 
+        bound = deflateBound (&priv->deflater.zstream, payload_length);
+        if (bound > G_MAXUINT) {
+                g_set_error_literal (error,
+                                     SOUP_WEBSOCKET_ERROR,
+                                     SOUP_WEBSOCKET_CLOSE_TOO_BIG,
+                                     "Outgoing WebSocket message is too large 
to compress");
+                g_bytes_unref (payload);
+                return NULL;
+        }
+
         /* Mark the frame as compressed using reserved bit 1 (0x40) */
         header[0] |= 0x40;
 
         buffer = g_byte_array_new ();
-        max_length = deflateBound(&priv->deflater.zstream, payload_length);
+        max_length = (guint)bound;
 
         priv->deflater.zstream.next_in = (void *)payload_data;
         priv->deflater.zstream.avail_in = payload_length;
@@ -333,6 +344,17 @@ soup_websocket_extension_deflate_process
                 if (priv->deflater.zstream.avail_out == 0) {
                         guint write_position;
 
+                        if (buffer->len > G_MAXUINT - max_length) {
+                                deflateReset (&priv->deflater.zstream);
+                                g_byte_array_unref (buffer);
+                                g_bytes_unref (payload);
+                                g_set_error_literal (error,
+                                                     SOUP_WEBSOCKET_ERROR,
+                                                     
SOUP_WEBSOCKET_CLOSE_TOO_BIG,
+                                                     "Compressed WebSocket 
message is too large");
+                                return NULL;
+                        }
+
                         priv->deflater.zstream.avail_out = max_length;
                         write_position = buffer->len;
                         g_byte_array_set_size (buffer, buffer->len + 
max_length);
diff -urp libsoup-2.74.3.orig/tests/websocket-test.c 
libsoup-2.74.3/tests/websocket-test.c
--- libsoup-2.74.3.orig/tests/websocket-test.c  2026-10-05 13:35:09.602907626 
-0500
+++ libsoup-2.74.3/tests/websocket-test.c       2026-10-05 13:37:27.434870030 
-0500
@@ -1770,6 +1770,48 @@ test_deflate_negotiate_direct (Test *tes
 }
 
 static void
+test_deflate_outgoing_too_large (void)
+{
+       SoupWebsocketExtension *extension;
+       GBytes *payload;
+       GBytes *output;
+       GError *error = NULL;
+       guint8 header = 0x82;
+       static const guint8 dummy[1] = { 0 };
+
+       if (sizeof (gsize) < 8) {
+               g_test_skip ("needs a 64-bit gsize");
+               return;
+       }
+
+       extension = g_object_new (SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE, NULL);
+       g_assert_true (soup_websocket_extension_configure (extension,
+                                                          
SOUP_WEBSOCKET_CONNECTION_CLIENT,
+                                                          NULL, &error));
+       g_assert_no_error (error);
+
+       /* The payload data is never read, so a bogus pointer is enough */
+       payload = g_bytes_new_static (dummy, (gsize)G_MAXUINT - 100);
+       output = soup_websocket_extension_process_outgoing_message (extension, 
&header,
+                                                                   payload, 
&error);
+       g_assert_error (error, SOUP_WEBSOCKET_ERROR, 
SOUP_WEBSOCKET_CLOSE_TOO_BIG);
+       g_assert_null (output);
+       /* The frame must not have been marked as compressed */
+       g_assert_cmpuint (header & 0x40, ==, 0);
+       g_clear_error (&error);
+
+       /* The extension must still be usable afterwards */
+       payload = g_bytes_new_static ("hello", 5);
+       output = soup_websocket_extension_process_outgoing_message (extension, 
&header,
+                                                                   payload, 
&error);
+       g_assert_no_error (error);
+       g_assert_nonnull (output);
+       g_bytes_unref (output);
+
+       g_object_unref (extension);
+}
+
+static void
 test_deflate_disabled_in_message_direct (Test *test,
                                         gconstpointer unused)
 {
@@ -2260,6 +2302,9 @@ main (int argc,
                    test_deflate_negotiate_direct,
                    NULL);
 
+       g_test_add_func ("/websocket/deflate/outgoing-too-large",
+                        test_deflate_outgoing_too_large);
+
        g_test_add ("/websocket/direct/deflate-disabled-in-message", Test, 
NULL, NULL,
                    test_deflate_disabled_in_message_direct,
                    NULL);

++++++ libsoup2-CVE-2026-12547.patch ++++++
>From c683326d3b05b7b263a62f9fea62a801134755ba Mon Sep 17 00:00:00 2001
From: Patrick Griffis <[email protected]>
Date: Fri, 4 Sep 2026 15:18:26 -0500
Subject: [PATCH] auth: Scope cached proxy credentials to the proxy they came
 from

priv->proxy_auth cached a single SoupAuth with no record of which proxy it
belonged to. After the session switched to a different proxy, the cached
credentials were sent to the new proxy, leaking them across proxies (and,
when the proxy configuration was removed entirely, to a direct connection).

Remember the authority the cached proxy auth was obtained from and only
reuse it for a message whose proxy authority matches.

Adds a regression test that uses two SoupServers as proxy endpoints and
fails without the fix.

Fixes #506 (CVE-2026-12547)
---
 libsoup/auth/soup-auth-manager.c |  54 +++++++++-
 tests/meson.build                |   1 +
 tests/proxy-auth-cache-test.c    | 166 +++++++++++++++++++++++++++++++
 3 files changed, 216 insertions(+), 5 deletions(-)
 create mode 100644 tests/proxy-auth-cache-test.c

Index: libsoup-2.74.3/libsoup/soup-auth-manager.c
===================================================================
--- libsoup-2.74.3.orig/libsoup/soup-auth-manager.c
+++ libsoup-2.74.3/libsoup/soup-auth-manager.c
@@ -69,6 +69,7 @@ struct SoupAuthManagerPrivate {
 
        GMutex lock;
        SoupAuth *proxy_auth;
+       char *proxy_auth_authority;
        GHashTable *auth_hosts;
 };
 
@@ -113,6 +114,7 @@ soup_auth_manager_finalize (GObject *obj
        g_hash_table_destroy (priv->auth_hosts);
 
        g_clear_object (&priv->proxy_auth);
+       g_clear_pointer (&priv->proxy_auth_authority, g_free);
 
        g_mutex_clear (&priv->lock);
 
@@ -498,6 +500,36 @@ lookup_auth (SoupAuthManagerPrivate *pri
        return NULL;
 }
 
+static char *
+get_proxy_authority (SoupMessage *msg)
+{
+       SoupConnection *conn = soup_message_get_connection (msg);
+       SoupURI *proxy_uri;
+       char *authority = NULL;
+
+       if (!conn)
+               return NULL;
+
+       proxy_uri = soup_connection_get_proxy_uri (conn);
+       if (proxy_uri)
+               authority = g_strdup_printf ("%s:%d", soup_uri_get_host 
(proxy_uri), soup_uri_get_port (proxy_uri));
+
+       return authority;
+}
+
+static gboolean
+proxy_auth_matches_msg (SoupAuthManagerPrivate *priv, SoupMessage *msg)
+{
+       char *authority = get_proxy_authority (msg);
+       gboolean matches;
+
+       matches = authority && priv->proxy_auth_authority &&
+                 g_strcmp0 (authority, priv->proxy_auth_authority) == 0;
+       g_free (authority);
+
+       return matches;
+}
+
 static SoupAuth *
 lookup_proxy_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
 {
@@ -511,7 +543,13 @@ lookup_proxy_auth (SoupAuthManagerPrivat
        if (soup_message_get_flags (msg) & SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE)
                return NULL;
 
-       return priv->proxy_auth;
+       /* Only reuse the cached proxy credentials for the same proxy they were
+        * obtained from, otherwise switching proxies would leak them.
+        */
+       if (priv->proxy_auth && proxy_auth_matches_msg (priv, msg))
+               return priv->proxy_auth;
+
+       return NULL;
 }
 
 static void
@@ -688,8 +726,9 @@ proxy_auth_got_headers (SoupMessage *msg
                        prior_auth_failed = TRUE;
        }
 
-       if (!(soup_message_get_flags (msg) & 
SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE))
-               auth = priv->proxy_auth ? g_object_ref (priv->proxy_auth) : 
NULL;
+       if (!(soup_message_get_flags (msg) & 
SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE) &&
+           priv->proxy_auth && proxy_auth_matches_msg (priv, msg))
+               auth = g_object_ref (priv->proxy_auth);
 
        if (!auth) {
                auth = create_auth (priv, msg);
@@ -697,8 +736,11 @@ proxy_auth_got_headers (SoupMessage *msg
                        g_mutex_unlock (&priv->lock);
                        return;
                }
-               if (!(soup_message_get_flags (msg) & 
SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE))
-                       priv->proxy_auth = g_object_ref (auth);
+               if (!(soup_message_get_flags (msg) & 
SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE)) {
+                       g_set_object (&priv->proxy_auth, auth);
+                       g_free (priv->proxy_auth_authority);
+                       priv->proxy_auth_authority = get_proxy_authority (msg);
+               }
        }
 
        /* If we need to authenticate, try to do it. */
Index: libsoup-2.74.3/tests/meson.build
===================================================================
--- libsoup-2.74.3.orig/tests/meson.build
+++ libsoup-2.74.3/tests/meson.build
@@ -48,6 +48,7 @@ tests = [
   ['hsts-db', true, []],
   ['misc', true, []],
   ['multipart', true, []],
+  ['proxy-auth-cache', false, []],
   ['no-ssl', true, []],
   ['ntlm', true, []],
   ['redirect', true, []],
Index: libsoup-2.74.3/tests/proxy-auth-cache-test.c
===================================================================
--- /dev/null
+++ libsoup-2.74.3/tests/proxy-auth-cache-test.c
@@ -0,0 +1,164 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2026 Igalia S.L.
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program.  If not, see 
<http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+#include "test-utils.h"
+
+/* Two SoupServers stand in as HTTP proxies. When a session uses an HTTP proxy
+ * for an http:// URL it sends the full request, including Proxy-Authorization,
+ * straight to the proxy, so a SoupServer can observe it without forwarding.
+ * After authenticating to the first proxy, switching the session to a second
+ * proxy must not send the first proxy's credentials to it. See issue #506.
+ */
+
+static gboolean second_proxy_saw_authorization;
+static gboolean second_proxy_hit;
+
+static void
+first_proxy_cb (SoupServer        *server,
+               SoupMessage *msg,
+               const char        *path,
+               GHashTable        *query,
+               SoupClientContext *context,
+               gpointer           user_data)
+{
+       /* Challenge until the client authenticates */
+       if (!soup_message_headers_get_one (msg->request_headers, 
"Proxy-Authorization")) {
+               soup_message_set_status (msg, SOUP_STATUS_PROXY_UNAUTHORIZED);
+               soup_message_headers_append (msg->response_headers,
+                                            "Proxy-Authenticate", "Basic 
realm=\"first\"");
+               return;
+       }
+
+       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_message_set_response (msg, "text/plain", SOUP_MEMORY_STATIC, "ok", 
2);
+}
+
+static void
+second_proxy_cb (SoupServer        *server,
+                SoupMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+               SoupClientContext *context,
+                gpointer           user_data)
+{
+       second_proxy_hit = TRUE;
+       if (soup_message_headers_get_one (msg->request_headers, 
"Proxy-Authorization"))
+               second_proxy_saw_authorization = TRUE;
+
+       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_message_set_response (msg, "text/plain", SOUP_MEMORY_STATIC, "ok", 
2);
+}
+
+static gboolean
+authenticate_cb (SoupSession *session,
+                SoupMessage *msg,
+                SoupAuth    *auth,
+                gboolean     retrying,
+                gpointer     user_data)
+{
+       if (soup_auth_is_for_proxy (auth) && !retrying) {
+               soup_auth_authenticate (auth, "user", "password");
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static void
+send_via_proxy (SoupSession *session, gboolean new_connection)
+{
+       SoupMessage *msg;
+
+       msg = soup_message_new (SOUP_METHOD_GET, "http://origin.test/";);
+
+       if (new_connection) {
+               SoupMessageFlags flags = soup_message_get_flags (msg);
+               soup_message_set_flags (msg, flags | 
SOUP_MESSAGE_NEW_CONNECTION);
+       }
+       g_signal_connect (session, "authenticate", G_CALLBACK 
(authenticate_cb), NULL);
+       soup_session_send_message (session, msg);
+
+       soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+
+       g_object_unref (msg);
+}
+
+static void
+do_proxy_switch_no_credential_leak (void)
+{
+       SoupServer *first, *second;
+       SoupURI *first_uri, *second_uri;
+       char *first_str, *second_str;
+       GProxyResolver *resolver;
+       SoupSession *session;
+
+       first = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
+       soup_server_add_handler (first, NULL, first_proxy_cb, NULL, NULL);
+       first_uri = soup_test_server_get_uri (first, "http", "127.0.0.1");
+       first_str = soup_uri_to_string (first_uri, FALSE);
+
+       second = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
+       soup_server_add_handler (second, NULL, second_proxy_cb, NULL, NULL);
+       second_uri = soup_test_server_get_uri (second, "http", "127.0.0.1");
+       second_str = soup_uri_to_string (second_uri, FALSE);
+
+       resolver = g_simple_proxy_resolver_new (first_str, NULL);
+       session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, 
"proxy-resolver", resolver, NULL);
+
+       /* Authenticate to the first proxy */
+       send_via_proxy (session, FALSE);
+
+       /* Switch the session to the second proxy and send again on a fresh
+        * connection so the request actually goes to the second proxy.
+        */
+       g_simple_proxy_resolver_set_default_proxy (G_SIMPLE_PROXY_RESOLVER 
(resolver), second_str);
+       second_proxy_hit = FALSE;
+       second_proxy_saw_authorization = FALSE;
+       send_via_proxy (session, TRUE);
+
+       /* Make sure the second request really reached the second proxy... */
+       g_assert_true (second_proxy_hit);
+       /* ...and that the first proxy's credentials did not leak to it */
+       g_assert_false (second_proxy_saw_authorization);
+
+       soup_test_session_abort_unref (session);
+       g_object_unref (resolver);
+       g_free (first_str);
+       g_free (second_str);
+       soup_uri_free (first_uri);
+       soup_uri_free (second_uri);
+       soup_test_server_quit_unref (first);
+       soup_test_server_quit_unref (second);
+}
+
+int
+main (int argc, char **argv)
+{
+       int ret;
+
+       test_init (argc, argv, NULL);
+
+       g_test_add_func ("/proxy-auth-cache/switch-no-credential-leak",
+                        do_proxy_switch_no_credential_leak);
+
+       ret = g_test_run ();
+
+       test_cleanup ();
+       return ret;
+}

++++++ libsoup2-CVE-2026-12548.patch ++++++
--- /var/tmp/diff_new_pack.pFOzxP/_old  2026-10-08 15:12:58.203703592 +0200
+++ /var/tmp/diff_new_pack.pFOzxP/_new  2026-10-08 15:12:58.207703760 +0200
@@ -29,7 +29,7 @@
 @@ -493,6 +493,14 @@
            SOUP_STATUS_BAD_REQUEST,
            NULL, NULL, -1,
-           { { NULL } }, 0
+           { { NULL } }
 +        },
 +
 +        { "Header fileds too large", NULL,
@@ -37,7 +37,7 @@
 +          (18 + 256) * 1024,
 +          SOUP_STATUS_BAD_REQUEST,
 +          NULL, NULL, -1,
-+          { { NULL } }, 0
++          { { NULL } }
        }
  };
  static const int num_reqtests = G_N_ELEMENTS (reqtests);

++++++ libsoup2-CVE-2026-15711.patch ++++++
>From 60aa1ce2bdc7bb5da33be9062f50bcec7db67fca Mon Sep 17 00:00:00 2001
From: Zayd Rajab <[email protected]>
Date: Thu, 16 Jul 2026 02:32:19 +0000
Subject: [PATCH] websocket: Reject oversized control frames

RFC 6455 section 5.5 limits control frame payloads to 125 bytes.
Reject extended payload length indicators as soon as the base header is
available, before waiting for additional length or payload bytes.

Add regression coverage for both extended length indicators and both
receive directions.

This is CVE-2026-15711.

Closes #515
---
 libsoup/websocket/soup-websocket-connection.c |   7 ++
 tests/websocket-test.c                        | 113 ++++++++++++++++++
 2 files changed, 120 insertions(+)

diff -urp libsoup-2.74.3.orig/libsoup/soup-websocket-connection.c 
libsoup-2.74.3/libsoup/soup-websocket-connection.c
--- libsoup-2.74.3.orig/libsoup/soup-websocket-connection.c     2026-09-28 
19:21:10.207636435 -0500
+++ libsoup-2.74.3/libsoup/soup-websocket-connection.c  2026-09-28 
19:34:48.026514214 -0500
@@ -1021,6 +1021,13 @@ process_frame (SoupWebsocketConnection *
                 return FALSE;
         }
 
+       /* RFC 6455 section 5.5 limits control frame payloads to 125 bytes. */
+       if (control && (header[1] & 0x7f) > 125) {
+               g_debug ("received oversized control frame");
+               protocol_error_and_close (self);
+               return FALSE;
+       }
+
        switch (header[1] & 0x7f) {
        case 126:
                /* If 126, the following 2 bytes interpreted as a 16-bit
diff -urp libsoup-2.74.3.orig/tests/websocket-test.c 
libsoup-2.74.3/tests/websocket-test.c
--- libsoup-2.74.3.orig/tests/websocket-test.c  2026-09-28 19:21:10.192045502 
-0500
+++ libsoup-2.74.3/tests/websocket-test.c       2026-09-28 19:34:48.027514241 
-0500
@@ -630,6 +630,98 @@ test_send_bad_data (Test *test,
        g_assert_cmpuint (soup_websocket_connection_get_close_code 
(test->client), ==, SOUP_WEBSOCKET_CLOSE_BAD_DATA);
 }
 
+static gboolean
+on_timeout_set_flag (gpointer user_data)
+{
+       gboolean *timed_out = user_data;
+
+       *timed_out = TRUE;
+
+       return G_SOURCE_REMOVE;
+}
+
+static void
+wait_for_websocket_error (GError **error)
+{
+       gboolean timed_out = FALSE;
+       guint timeout_id;
+
+       timeout_id = g_timeout_add_seconds (1, on_timeout_set_flag, &timed_out);
+       WAIT_UNTIL (*error != NULL || timed_out);
+
+       if (!timed_out)
+               g_source_remove (timeout_id);
+       g_assert_false (timed_out);
+}
+
+static void
+write_oversized_control_frame_header (GIOStream *io,
+                                     guint8 length_indicator,
+                                     gboolean masked)
+{
+       guint8 frame[] = { 0x8a, length_indicator };
+       GError *error = NULL;
+       gsize written;
+
+       g_assert_true (length_indicator == 126 || length_indicator == 127);
+
+       if (masked)
+               frame[1] |= 0x80;
+
+       g_output_stream_write_all (g_io_stream_get_output_stream (io),
+                                  frame, sizeof (frame), &written, NULL, 
&error);
+       g_assert_no_error (error);
+       g_assert_cmpuint (written, ==, sizeof (frame));
+}
+
+typedef struct {
+       guint8 length_indicator;
+       gboolean server_receives;
+} OversizedControlFrameTest;
+
+static void
+test_receive_oversized_control_frame (Test *test,
+                                     gconstpointer data)
+{
+       const OversizedControlFrameTest *config = data;
+       SoupWebsocketConnection *receiver;
+       SoupWebsocketConnection *sender;
+       GError *error = NULL;
+       GIOStream *io;
+       gulong error_id;
+       gboolean close_event = FALSE;
+
+       if (config->server_receives) {
+               receiver = test->server;
+               sender = test->client;
+       } else {
+               receiver = test->client;
+               sender = test->server;
+       }
+
+       g_signal_handlers_disconnect_by_func (receiver, on_error_not_reached, 
NULL);
+       error_id = g_signal_connect (receiver, "error", G_CALLBACK 
(on_error_copy), &error);
+       g_signal_connect (sender, "closed", G_CALLBACK (on_close_set_flag), 
&close_event);
+
+       io = soup_websocket_connection_get_io_stream (sender);
+       write_oversized_control_frame_header (io, config->length_indicator,
+                                             config->server_receives);
+       wait_for_websocket_error (&error);
+       g_assert_error (error, SOUP_WEBSOCKET_ERROR, 
SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR);
+       g_clear_error (&error);
+       g_signal_handler_disconnect (receiver, error_id);
+
+       WAIT_UNTIL (soup_websocket_connection_get_state (sender) == 
SOUP_WEBSOCKET_STATE_CLOSED);
+       g_assert_true (close_event);
+       g_assert_cmpuint (soup_websocket_connection_get_close_code (sender), ==,
+                         SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR);
+}
+
+static const OversizedControlFrameTest oversized_control_frame_16_server = { 
126, TRUE };
+static const OversizedControlFrameTest oversized_control_frame_64_server = { 
127, TRUE };
+static const OversizedControlFrameTest oversized_control_frame_16_client = { 
126, FALSE };
+static const OversizedControlFrameTest oversized_control_frame_64_client = { 
127, FALSE };
+
 static const char *negotiate_client_protocols[] = { "bbb", "ccc", NULL };
 static const char *negotiate_server_protocols[] = { "aaa", "bbb", "ccc", NULL 
};
 static const char *negotiated_protocol = "bbb";
@@ -2009,6 +2101,27 @@ main (int argc,
                    test_send_bad_data,
                    teardown_soup_connection);
 
+       g_test_add 
("/websocket/direct/server-receive-oversized-control-frame/16-bit",
+                   Test, &oversized_control_frame_16_server,
+                   setup_direct_connection,
+                   test_receive_oversized_control_frame,
+                   teardown_direct_connection);
+       g_test_add 
("/websocket/direct/server-receive-oversized-control-frame/64-bit",
+                   Test, &oversized_control_frame_64_server,
+                   setup_direct_connection,
+                   test_receive_oversized_control_frame,
+                   teardown_direct_connection);
+       g_test_add 
("/websocket/direct/client-receive-oversized-control-frame/16-bit",
+                   Test, &oversized_control_frame_16_client,
+                   setup_direct_connection,
+                   test_receive_oversized_control_frame,
+                   teardown_direct_connection);
+       g_test_add 
("/websocket/direct/client-receive-oversized-control-frame/64-bit",
+                   Test, &oversized_control_frame_64_client,
+                   setup_direct_connection,
+                   test_receive_oversized_control_frame,
+                   teardown_direct_connection);
+
        g_test_add ("/websocket/direct/close-clean-client", Test, NULL, NULL,
                    test_close_clean_client_direct,
                    NULL);

++++++ libsoup2-CVE-2026-15714.patch ++++++
>From 79a52cadc490360e249cc2b23038d532b44dbf23 Mon Sep 17 00:00:00 2001
From: Mike Gorse <[email protected]>
Date: Fri, 4 Sep 2026 14:10:46 -0500
Subject: [PATCH] multipart: Fix out-of-bounds read in case of an excessively
 long boundary

soup_multipart_input_stream_read_headers() compared meta_buf against the
boundary using the boundary length without checking that meta_buf held
that many bytes, so a server sending a boundary longer than the read
buffer could make strncmp() read past the end of the allocation. It also
computed read_buf + nread - 4 before verifying nread was large enough,
and asserted rather than failing when a line did not end in a newline.

Only compare once enough bytes have been read, index read_buf after the
length checks, and treat an unterminated line as end of parsing.

Closes !546
---
 libsoup/soup-multipart-input-stream.c | 12 ++++-----
 tests/multipart-test.c                | 37 +++++++++++++++++++++++++++
 2 files changed, 43 insertions(+), 6 deletions(-)

Index: libsoup-2.74.3/libsoup/soup-multipart-input-stream.c
===================================================================
--- libsoup-2.74.3.orig/libsoup/soup-multipart-input-stream.c
+++ libsoup-2.74.3/libsoup/soup-multipart-input-stream.c
@@ -348,7 +348,6 @@ soup_multipart_input_stream_read_headers
 {
        SoupMultipartInputStreamPrivate *priv = multipart->priv;
        guchar read_buf[RESPONSE_BLOCK_SIZE];
-       guchar *buf;
        gboolean got_boundary = FALSE;
        gboolean got_lf = FALSE;
        gssize nread = 0;
@@ -370,22 +369,23 @@ soup_multipart_input_stream_read_headers
                 * may get the multipart end indicator without getting a new 
line.
                 */
                if (!got_boundary &&
+                   priv->meta_buf->len >= priv->boundary_size &&
                    !strncmp ((char *)priv->meta_buf->data,
                              priv->boundary,
                              priv->boundary_size)) {
                        got_boundary = TRUE;
 
                        /* Now check for possible multipart termination. */
-                       buf = &read_buf[nread - 4];
-                       if ((nread >= 4 && !memcmp (buf, "--\r\n", 4)) ||
-                           (nread >= 3 && !memcmp (buf + 1, "--\n", 3)) ||
-                           (nread >= 3 && !memcmp (buf + 2, "--", 2))) {
+                       if ((nread >= 4 && !memcmp (read_buf + nread - 4, 
"--\r\n", 4)) ||
+                           (nread >= 3 && !memcmp (read_buf + nread - 3, 
"--\n", 3)) ||
+                           (nread >= 3 && !memcmp (read_buf + nread - 2, "--", 
2))) {
                                g_byte_array_set_size (priv->meta_buf, 0);
                                return FALSE;
                        }
                }
 
-               g_return_val_if_fail (got_lf, FALSE);
+               if (!got_lf)
+                       return FALSE;
 
                /* Discard pre-boundary lines. */
                if (!got_boundary) {
Index: libsoup-2.74.3/tests/multipart-test.c
===================================================================
--- libsoup-2.74.3.orig/tests/multipart-test.c
+++ libsoup-2.74.3/tests/multipart-test.c
@@ -413,6 +413,40 @@ sync_multipart_handling_cb (GObject *sou
 }
 
 static void
+test_multipart_bounds_bad_4 (void)
+{
+        SoupMessage *msg;
+        GInputStream *in;
+        GInputStream *next_part;
+        SoupMultipartInputStream *multipart;
+        GError *error = NULL;
+        gchar *boundary;
+        gchar *content_type;
+        gchar *raw_data;
+
+        boundary = g_new (gchar, 20001);
+        memset (boundary, 'a', 20000);
+        boundary[20000] = '\0';
+
+        msg = soup_message_new(SOUP_METHOD_POST, "http://foo/upload";);
+        content_type = g_strconcat ("multipart/form-data; boundary=\"", 
boundary, "\"", NULL);
+        soup_message_headers_replace (msg->response_headers, "Content-Type", 
content_type);
+
+        raw_data = g_strconcat ("--", boundary, "\r\nContent-Type: 
text-plain\r\n\r\nX\r\n", NULL);
+        in = g_memory_input_stream_new_from_data (raw_data, strlen (raw_data), 
NULL);
+        multipart = soup_multipart_input_stream_new (msg, in);
+        g_object_unref (in);
+        next_part = soup_multipart_input_stream_next_part (multipart, NULL, 
&error);
+        g_assert_no_error (error);
+        g_assert_null (next_part);
+        g_object_unref (multipart);
+        g_object_unref (msg);
+        g_free (raw_data);
+        g_free (content_type);
+        g_free (boundary);
+}
+
+static void
 test_multipart (gconstpointer data)
 {
        int headers_expected = 1, sniffed_expected = 1;
@@ -508,6 +542,7 @@ main (int argc, char **argv)
        g_test_add_data_func ("/multipart/sync", GINT_TO_POINTER 
(SYNC_MULTIPART), test_multipart);
        g_test_add_data_func ("/multipart/async", GINT_TO_POINTER 
(ASYNC_MULTIPART), test_multipart);
        g_test_add_data_func ("/multipart/async-small-reads", GINT_TO_POINTER 
(ASYNC_MULTIPART_SMALL_READS), test_multipart);
+        g_test_add_func ("/multipart/bounds-bad-4", 
test_multipart_bounds_bad_4);
 
        ret = g_test_run ();
 

++++++ libsoup2-CVE-2026-2708.patch ++++++
--- /var/tmp/diff_new_pack.pFOzxP/_old  2026-10-08 15:12:58.337709208 +0200
+++ /var/tmp/diff_new_pack.pFOzxP/_new  2026-10-08 15:12:58.342709418 +0200
@@ -88,7 +88,7 @@
 +          -1,
 +          SOUP_STATUS_OK,
 +          "POST", "/", SOUP_HTTP_1_1,
-+          { { "Content-Length", "4" } }, 0
++          { { "Content-Length", "4" } }
 +        },
 +
 +        { "Duplicate Content-Length with the same decimal value", NULL,
@@ -96,7 +96,7 @@
 +          -1,
 +          SOUP_STATUS_OK,
 +          "POST", "/", SOUP_HTTP_1_1,
-+          { { "Content-Length", "04" } }, 0
++          { { "Content-Length", "04" } }
 +        },
 +
        /************************/
@@ -114,7 +114,7 @@
 +          -1,
 +          SOUP_STATUS_BAD_REQUEST,
 +          NULL, NULL, -1,
-+          { { NULL } }, 0
++          { { NULL } }
        }
  };
  static const int num_reqtests = G_N_ELEMENTS (reqtests);

++++++ libsoup2-CVE-2026-66339.patch ++++++
>From 6ed1711ae498f91c78fc12d960f1298c32c5d01d Mon Sep 17 00:00:00 2001
From: Patrick Griffis <[email protected]>
Date: Fri, 4 Sep 2026 15:18:26 -0500
Subject: [PATCH] auth: Don't send Proxy-Authorization through an established
 tunnel

After a CONNECT tunnel to an HTTPS origin is set up, the actual request
travels inside the tunnel directly to the origin server. auth_msg_starting()
attached Proxy-Authorization to every message including these, leaking the
proxy credentials to the origin.

Skip the proxy-auth handling entirely when the message's connection is a
tunnel, and clear any Proxy-Authorization header from it.

The proxy test suite requires Apache and a CONNECT-tunnel-with-TLS setup
that the in-tree harness does not provide, so this has no automated
regression test here.

Fixes #534 (CVE-2026-66339)
---
 libsoup/auth/soup-auth-manager.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

Index: libsoup-2.74.3/libsoup/soup-auth-manager.c
===================================================================
--- libsoup-2.74.3.orig/libsoup/soup-auth-manager.c
+++ libsoup-2.74.3/libsoup/soup-auth-manager.c
@@ -15,6 +15,7 @@
 #include "soup.h"
 #include "soup-connection-auth.h"
 #include "soup-message-private.h"
+#include "soup-connection.h"
 #include "soup-message-queue.h"
 #include "soup-path-map.h"
 #include "soup-session-private.h"
@@ -734,6 +735,8 @@ auth_msg_starting (SoupMessage *msg, gpo
 {
        SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER (manager)->priv;
        SoupAuth *auth;
+       SoupConnection *conn;
+       gboolean tunnelled;
 
        if (soup_message_get_flags (msg) & SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE)
                return;
@@ -751,6 +754,22 @@ auth_msg_starting (SoupMessage *msg, gpo
                update_authorization_header (msg, auth, FALSE);
        }
 
+       /* Proxy-Authorization must only be sent to the proxy itself, never on
+        * a request travelling through an established CONNECT tunnel to the
+        * origin server, which would leak the proxy credentials to the origin.
+        * The CONNECT request that sets the tunnel up is addressed to the
+        * proxy, so it still needs the header.
+        */
+       conn = soup_message_get_connection (msg);
+       tunnelled = conn && soup_connection_is_tunnelled (conn) &&
+                   msg->method != SOUP_METHOD_CONNECT;
+       if (tunnelled) {
+               soup_message_set_proxy_auth (msg, NULL);
+               update_authorization_header (msg, NULL, TRUE);
+               g_mutex_unlock (&priv->lock);
+               return;
+       }
+
        auth = lookup_proxy_auth (priv, msg);
        if (auth) {
                authenticate_auth (manager, auth, msg, FALSE, TRUE, FALSE);

Reply via email to