public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/libsoup3] f45: Update to 3.7.3 and drop upstreamed CVE patches
@ 2026-09-24 18:37 Petr Schindler
0 siblings, 0 replies; only message in thread
From: Petr Schindler @ 2026-09-24 18:37 UTC (permalink / raw)
To: git-commits
A new commit has been pushed.
Repo : rpms/libsoup3
Branch : f45
Commit : 8323f6109b28bfc9248ff4810e36c464d4b5f3ee
Author : Petr Schindler <pschindl@redhat.com>
Date : 2026-09-24T20:26:32+02:00
Stats : +2/-1952 in 8 file(s)
URL : https://src.fedoraproject.org/rpms/libsoup3/c/8323f6109b28bfc9248ff4810e36c464d4b5f3ee?branch=f45
Log:
Update to 3.7.3 and drop upstreamed CVE patches
---
diff --git a/CVE-2026-15709.patch b/CVE-2026-15709.patch
deleted file mode 100644
index 799c378..0000000
--- a/CVE-2026-15709.patch
+++ /dev/null
@@ -1,782 +0,0 @@
-From a4294044466df66ea98492d67bb74172ed3f8762 Mon Sep 17 00:00:00 2001
-From: Zayd Rajab <zaydr@amazon.com>
-Date: Fri, 4 Sep 2026 14:17:23 -0500
-Subject: [PATCH] websocket: Bound decompressed message size during inflation
-
-Pass each connection's remaining max-total-message-size allowance through
-the generic WebSocket extension processing path via a new
-process_incoming_message_with_limit vfunc, falling back to
-process_incoming_message for extensions that don't implement it. Bound
-permessage-deflate output growth to that allowance and use a one-byte
-probe to detect output beyond an exact boundary without allocating
-another normal output chunk.
-
-Preserve the documented unlimited default for clients and direct
-connections. SoupServer continues applying its existing 128 KiB default,
-while callers can configure the limit explicitly in either receive
-direction.
-
-Add boundary, generic-extension fallback, receive-direction, SoupServer,
-fragmentation, and unlimited-mode coverage.
-
-This addresses CVE-2026-15709.
-
-Reported-by: Tristan Madani <secientist@gmail.com>
-Closes !543
----
- libsoup/websocket/soup-websocket-connection.c | 34 +-
- .../soup-websocket-extension-deflate.c | 144 ++++++---
- libsoup/websocket/soup-websocket-extension.c | 53 +++
- libsoup/websocket/soup-websocket-extension.h | 14 +-
- tests/websocket-test.c | 306 +++++++++++++++++-
- 5 files changed, 506 insertions(+), 45 deletions(-)
-
-diff --git a/libsoup/websocket/soup-websocket-connection.c b/libsoup/websocket/soup-websocket-connection.c
-index ffd713ef..f07d4219 100644
---- a/libsoup/websocket/soup-websocket-connection.c
-+++ b/libsoup/websocket/soup-websocket-connection.c
-@@ -990,8 +990,15 @@ process_contents (SoupWebsocketConnection *self,
- case 0x02:
- /* Safety valve */
- if (priv->max_total_message_size > 0 &&
-- (priv->message_data->len + payload_len) > priv->max_total_message_size) {
-- too_big_message_error_and_close (self, (priv->message_data->len + payload_len));
-+ (priv->message_data->len > priv->max_total_message_size ||
-+ payload_len > priv->max_total_message_size - priv->message_data->len)) {
-+ guint64 message_size = priv->message_data->len;
-+
-+ if (payload_len > G_MAXUINT64 - message_size)
-+ message_size = G_MAXUINT64;
-+ else
-+ message_size += payload_len;
-+ too_big_message_error_and_close (self, message_size);
- return;
- }
- g_byte_array_append (priv->message_data, payload, payload_len);
-@@ -1036,6 +1043,21 @@ process_contents (SoupWebsocketConnection *self,
- }
- }
-
-+static guint64
-+get_remaining_message_size (SoupWebsocketConnectionPrivate *priv)
-+{
-+ if (priv->max_total_message_size == 0)
-+ return G_MAXUINT64;
-+
-+ if (!priv->message_data)
-+ return priv->max_total_message_size;
-+
-+ if (priv->message_data->len >= priv->max_total_message_size)
-+ return 0;
-+
-+ return priv->max_total_message_size - priv->message_data->len;
-+}
-+
- static gboolean
- process_frame (SoupWebsocketConnection *self)
- {
-@@ -1051,6 +1073,7 @@ process_frame (SoupWebsocketConnection *self)
- gsize len;
- gsize at;
- GBytes *filtered_bytes;
-+ guint64 max_output_size;
- GList *l;
- GError *error = NULL;
-
-@@ -1165,11 +1188,16 @@ process_frame (SoupWebsocketConnection *self)
- }
-
- filtered_bytes = g_bytes_new_static (payload, payload_len);
-+ max_output_size = get_remaining_message_size (priv);
- for (l = priv->extensions; l != NULL; l = g_list_next (l)) {
- SoupWebsocketExtension *extension;
-
- extension = (SoupWebsocketExtension *)l->data;
-- filtered_bytes = soup_websocket_extension_process_incoming_message (extension, priv->incoming->data, filtered_bytes, &error);
-+ filtered_bytes = soup_websocket_extension_process_incoming_message_with_limit (extension,
-+ priv->incoming->data,
-+ filtered_bytes,
-+ max_output_size,
-+ &error);
- if (error) {
- emit_error_and_close (self, error, FALSE);
- return FALSE;
-diff --git a/libsoup/websocket/soup-websocket-extension-deflate.c b/libsoup/websocket/soup-websocket-extension-deflate.c
-index fa98a2e2..2c649a88 100644
---- a/libsoup/websocket/soup-websocket-extension-deflate.c
-+++ b/libsoup/websocket/soup-websocket-extension-deflate.c
-@@ -359,19 +359,29 @@ soup_websocket_extension_deflate_process_outgoing_message (SoupWebsocketExtensio
- return g_byte_array_free_to_bytes (buffer);
- }
-
-+static void
-+inflater_reset (Inflater *inflater)
-+{
-+ inflateReset (&inflater->zstream);
-+ inflater->uncompress_ongoing = FALSE;
-+}
-+
- static GBytes *
--soup_websocket_extension_deflate_process_incoming_message (SoupWebsocketExtension *extension,
-- guint8 *header,
-- GBytes *payload,
-- GError **error)
-+soup_websocket_extension_deflate_process_incoming_message_with_limit (SoupWebsocketExtension *extension,
-+ guint8 *header,
-+ GBytes *payload,
-+ guint64 max_output_size,
-+ GError **error)
- {
- const guint8 *payload_data;
- gsize payload_length;
- gboolean fin, control, compressed;
- GByteArray *buffer;
-- gsize bytes_read, bytes_written;
-+ gsize input_offset, bytes_written;
- int result;
- gboolean tail_added = FALSE;
-+ gboolean using_limit_probe = FALSE;
-+ guint8 limit_probe;
- SoupWebsocketExtensionDeflatePrivate *priv;
-
- priv = soup_websocket_extension_deflate_get_instance_private (SOUP_WEBSOCKET_EXTENSION_DEFLATE (extension));
-@@ -394,6 +404,7 @@ soup_websocket_extension_deflate_process_incoming_message (SoupWebsocketExtensio
- SOUP_WEBSOCKET_ERROR,
- SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR,
- "Received a non-first frame with RSV1 flag set");
-+ inflater_reset (&priv->inflater);
- g_bytes_unref (payload);
- return NULL;
- }
-@@ -410,64 +421,122 @@ soup_websocket_extension_deflate_process_incoming_message (SoupWebsocketExtensio
-
- buffer = g_byte_array_new ();
-
-- bytes_read = 0;
-- priv->inflater.zstream.next_in = (void *)payload_data;
-- priv->inflater.zstream.avail_in = payload_length;
--
-+ input_offset = 0;
-+ priv->inflater.zstream.avail_in = 0;
- bytes_written = 0;
- priv->inflater.zstream.avail_out = 0;
-
-- do {
-- gsize read_remaining;
-- gsize write_remaining;
-+ while (TRUE) {
-+ uInt input_before, output_before;
-+ uInt input_consumed, output_produced;
-
-- if (priv->inflater.zstream.avail_out == 0) {
-- guint current_position;
-+ if (priv->inflater.zstream.avail_in == 0 && input_offset < payload_length) {
-+ gsize input_length = MIN (payload_length - input_offset, G_MAXUINT);
-
-- priv->inflater.zstream.avail_out = BUFFER_SIZE;
-- current_position = buffer->len;
-- g_byte_array_set_size (buffer, buffer->len + BUFFER_SIZE);
-- priv->inflater.zstream.next_out = buffer->data + current_position;
-+ priv->inflater.zstream.next_in = (void *)(payload_data + input_offset);
-+ priv->inflater.zstream.avail_in = (uInt)input_length;
-+ input_offset += input_length;
- }
-
-- if (priv->inflater.zstream.avail_in == 0 && !tail_added && fin) {
-+ if (priv->inflater.zstream.avail_in == 0 &&
-+ input_offset == payload_length &&
-+ !tail_added && fin) {
- /* Append 4 octets of 0x00 0x00 0xff 0xff to the tail end */
- priv->inflater.zstream.next_in = (void *)"\x00\x00\xff\xff";
- priv->inflater.zstream.avail_in = 4;
-- bytes_read = 0;
- tail_added = TRUE;
- }
-
-- read_remaining = tail_added ? 4 : payload_length - bytes_read;
-- write_remaining = buffer->len - bytes_written;
-+ if (priv->inflater.zstream.avail_out == 0) {
-+ guint output_length = BUFFER_SIZE;
-+
-+ if (bytes_written >= max_output_size || buffer->len == G_MAXUINT) {
-+ priv->inflater.zstream.next_out = &limit_probe;
-+ priv->inflater.zstream.avail_out = 1;
-+ using_limit_probe = TRUE;
-+ } else {
-+ guint64 remaining = max_output_size - bytes_written;
-+ guint current_position = buffer->len;
-+
-+ output_length = MIN ((guint64)output_length, remaining);
-+ output_length = MIN (output_length, G_MAXUINT - buffer->len);
-+ g_assert (output_length > 0);
-+
-+ g_byte_array_set_size (buffer, buffer->len + output_length);
-+ priv->inflater.zstream.next_out = buffer->data + current_position;
-+ priv->inflater.zstream.avail_out = output_length;
-+ using_limit_probe = FALSE;
-+ }
-+ }
-+
-+ input_before = priv->inflater.zstream.avail_in;
-+ output_before = priv->inflater.zstream.avail_out;
- result = inflate (&priv->inflater.zstream, tail_added ? Z_FINISH : Z_NO_FLUSH);
-- bytes_read += read_remaining - priv->inflater.zstream.avail_in;
-- bytes_written += write_remaining - priv->inflater.zstream.avail_out;
-+ input_consumed = input_before - priv->inflater.zstream.avail_in;
-+ output_produced = output_before - priv->inflater.zstream.avail_out;
-+
-+ if (using_limit_probe && output_produced > 0)
-+ goto output_too_large;
-+
-+ bytes_written += output_produced;
-+
- if (!tail_added && result == Z_STREAM_END) {
- /* Received a block with BFINAL set to 1. Reset decompression state. */
- result = inflateReset (&priv->inflater.zstream);
- }
-
-- if ((!fin && bytes_read == payload_length) || (fin && tail_added && bytes_read == 4))
-- break;
-- } while (result == Z_OK || result == Z_BUF_ERROR);
--
-- g_bytes_unref (payload);
-+ if (result != Z_OK && result != Z_BUF_ERROR)
-+ goto invalid_data;
-
-- if (result != Z_OK && result != Z_BUF_ERROR) {
-- priv->inflater.uncompress_ongoing = FALSE;
-- g_set_error_literal (error,
-- SOUP_WEBSOCKET_ERROR,
-- SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR,
-- "Failed to uncompress incoming frame");
-- g_byte_array_unref (buffer);
-+ if (priv->inflater.zstream.avail_in == 0 &&
-+ input_offset == payload_length &&
-+ ((!fin && !tail_added) || (fin && tail_added)) &&
-+ priv->inflater.zstream.avail_out > 0)
-+ break;
-
-- return NULL;
-+ if (input_consumed == 0 && output_produced == 0)
-+ goto invalid_data;
- }
-
-+ g_bytes_unref (payload);
- g_byte_array_set_size (buffer, bytes_written);
-
- return g_byte_array_free_to_bytes (buffer);
-+
-+output_too_large:
-+ inflater_reset (&priv->inflater);
-+ g_bytes_unref (payload);
-+ g_byte_array_unref (buffer);
-+ g_set_error_literal (error,
-+ SOUP_WEBSOCKET_ERROR,
-+ SOUP_WEBSOCKET_CLOSE_TOO_BIG,
-+ "Decompressed WebSocket message exceeds configured maximum size");
-+
-+ return NULL;
-+
-+invalid_data:
-+ inflater_reset (&priv->inflater);
-+ g_bytes_unref (payload);
-+ g_byte_array_unref (buffer);
-+ g_set_error_literal (error,
-+ SOUP_WEBSOCKET_ERROR,
-+ SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR,
-+ "Failed to uncompress incoming frame");
-+
-+ return NULL;
-+}
-+
-+static GBytes *
-+soup_websocket_extension_deflate_process_incoming_message (SoupWebsocketExtension *extension,
-+ guint8 *header,
-+ GBytes *payload,
-+ GError **error)
-+{
-+ return soup_websocket_extension_deflate_process_incoming_message_with_limit (extension,
-+ header,
-+ payload,
-+ G_MAXUINT64,
-+ error);
- }
-
- static void
-@@ -483,6 +552,7 @@ soup_websocket_extension_deflate_class_init (SoupWebsocketExtensionDeflateClass
- extension_class->get_response_params = soup_websocket_extension_deflate_get_response_params;
- extension_class->process_outgoing_message = soup_websocket_extension_deflate_process_outgoing_message;
- extension_class->process_incoming_message = soup_websocket_extension_deflate_process_incoming_message;
-+ extension_class->process_incoming_message_with_limit = soup_websocket_extension_deflate_process_incoming_message_with_limit;
-
- object_class->finalize = soup_websocket_extension_deflate_finalize;
- }
-diff --git a/libsoup/websocket/soup-websocket-extension.c b/libsoup/websocket/soup-websocket-extension.c
-index 8882d2c0..95719e8c 100644
---- a/libsoup/websocket/soup-websocket-extension.c
-+++ b/libsoup/websocket/soup-websocket-extension.c
-@@ -47,6 +47,9 @@
- * before it's sent. Reserved bits of the header should be changed.
- * @process_incoming_message: called to process the payload data of a message
- * after it's received. Reserved bits of the header should be cleared.
-+ * @process_incoming_message_with_limit: called to process the payload data of
-+ * a message after it's received without exceeding the requested maximum
-+ * output size. If unset, @process_incoming_message is used. Since 3.8
- *
- * The class structure for the [class@WebsocketExtension].
- */
-@@ -216,3 +219,53 @@ soup_websocket_extension_process_incoming_message (SoupWebsocketExtension *exten
-
- return klass->process_incoming_message (extension, header, payload, error);
- }
-+
-+/**
-+ * soup_websocket_extension_process_incoming_message_with_limit:
-+ * @extension: a #SoupWebsocketExtension
-+ * @header: (inout): the message header
-+ * @payload: (transfer full): the payload data
-+ * @max_output_size: the maximum size in bytes of the processed payload
-+ * @error: return location for a #GError
-+ *
-+ * Process a message after it's received, without producing more than
-+ * @max_output_size bytes of output.
-+ *
-+ * This behaves like [method@WebsocketExtension.process_incoming_message],
-+ * but extensions that expand their input (such as `permessage-deflate`)
-+ * stop and return an error with [error@WebsocketError.CLOSE_TOO_BIG]
-+ * instead of producing output larger than @max_output_size. Extensions
-+ * that don't implement this fall back to
-+ * [method@WebsocketExtension.process_incoming_message].
-+ *
-+ * Returns: (transfer full): the message payload data, or %NULL in case of error
-+ *
-+ * Since: 3.8
-+ */
-+GBytes *
-+soup_websocket_extension_process_incoming_message_with_limit (SoupWebsocketExtension *extension,
-+ guint8 *header,
-+ GBytes *payload,
-+ guint64 max_output_size,
-+ GError **error)
-+{
-+ SoupWebsocketExtensionClass *klass;
-+
-+ g_return_val_if_fail (SOUP_IS_WEBSOCKET_EXTENSION (extension), NULL);
-+ g_return_val_if_fail (header != NULL, NULL);
-+ g_return_val_if_fail (payload != NULL, NULL);
-+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
-+
-+ klass = SOUP_WEBSOCKET_EXTENSION_GET_CLASS (extension);
-+ if (!klass->process_incoming_message_with_limit)
-+ return soup_websocket_extension_process_incoming_message (extension,
-+ header,
-+ payload,
-+ error);
-+
-+ return klass->process_incoming_message_with_limit (extension,
-+ header,
-+ payload,
-+ max_output_size,
-+ error);
-+}
-diff --git a/libsoup/websocket/soup-websocket-extension.h b/libsoup/websocket/soup-websocket-extension.h
-index 1931a264..a9d5e35c 100644
---- a/libsoup/websocket/soup-websocket-extension.h
-+++ b/libsoup/websocket/soup-websocket-extension.h
-@@ -55,8 +55,14 @@ struct _SoupWebsocketExtensionClass {
- GBytes *payload,
- GError **error);
-
-+ GBytes *(* process_incoming_message_with_limit) (SoupWebsocketExtension *extension,
-+ guint8 *header,
-+ GBytes *payload,
-+ guint64 max_output_size,
-+ GError **error);
-+
- /* <private> */
-- gpointer padding[6];
-+ gpointer padding[5];
- };
-
- SOUP_AVAILABLE_IN_ALL
-@@ -80,5 +86,11 @@ GBytes *soup_websocket_extension_process_incoming_message (Soup
- guint8 *header,
- GBytes *payload,
- GError **error);
-+SOUP_AVAILABLE_IN_3_8
-+GBytes *soup_websocket_extension_process_incoming_message_with_limit (SoupWebsocketExtension *extension,
-+ guint8 *header,
-+ GBytes *payload,
-+ guint64 max_output_size,
-+ GError **error);
-
- G_END_DECLS
-diff --git a/tests/websocket-test.c b/tests/websocket-test.c
-index db088a55..5f94b5f6 100644
---- a/tests/websocket-test.c
-+++ b/tests/websocket-test.c
-@@ -73,6 +73,18 @@ on_error_copy (SoupWebsocketConnection *ws,
- *copy = g_error_copy (error);
- }
-
-+static void
-+on_error_copy_once (SoupWebsocketConnection *ws,
-+ GError *error,
-+ gpointer user_data)
-+{
-+ GError **copy = user_data;
-+
-+ g_assert_null (*copy);
-+ *copy = g_error_copy (error);
-+ g_signal_handlers_disconnect_by_func (ws, G_CALLBACK (on_error_copy_once), user_data);
-+}
-+
- static void
- setup_listener (Test *test)
- {
-@@ -649,7 +661,7 @@ test_send_big_packets_soup (Test *test,
- g_assert_cmpuint (soup_websocket_connection_get_max_incoming_payload_size (test->client), ==, 128 * 1024);
- g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (test->client), ==, 0);
-
-- /* Max total message size defaults to 0 (unlimited), but SoupServer applies its own limit by default. */
-+ /* SoupServer applies its own total message size limit by default. */
- g_assert_cmpuint (soup_websocket_connection_get_max_incoming_payload_size (test->server), ==, 128 * 1024);
- g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (test->server), ==, 128 * 1024);
-
-@@ -756,9 +768,7 @@ test_send_exceeding_server_max_message_size (Test *test,
- soup_websocket_connection_set_max_total_message_size (test->client, 0);
- g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (test->client), ==, 0);
-
-- /* Set the server message total message size manually, because its
-- * default is different for direct connection vs. soup connection.
-- */
-+ /* The direct server connection defaults to unlimited. */
- soup_websocket_connection_set_max_total_message_size (test->server, 128 * 1024);
- g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (test->server), ==, 128 * 1024);
-
-@@ -1670,6 +1680,222 @@ send_fragments_server_thread (gpointer user_data)
- return NULL;
- }
-
-+typedef struct {
-+ SoupWebsocketExtension parent_instance;
-+} LegacyWebsocketExtension;
-+
-+typedef struct {
-+ SoupWebsocketExtensionClass parent_class;
-+} LegacyWebsocketExtensionClass;
-+
-+GType legacy_websocket_extension_get_type (void);
-+
-+#define LEGACY_TYPE_WEBSOCKET_EXTENSION (legacy_websocket_extension_get_type ())
-+G_DEFINE_TYPE (LegacyWebsocketExtension, legacy_websocket_extension, SOUP_TYPE_WEBSOCKET_EXTENSION)
-+
-+static gboolean legacy_extension_processed;
-+
-+static GBytes *
-+legacy_websocket_extension_process_incoming_message (SoupWebsocketExtension *extension,
-+ guint8 *header,
-+ GBytes *payload,
-+ GError **error)
-+{
-+ legacy_extension_processed = TRUE;
-+
-+ return payload;
-+}
-+
-+static void
-+legacy_websocket_extension_class_init (LegacyWebsocketExtensionClass *klass)
-+{
-+ SoupWebsocketExtensionClass *extension_class = SOUP_WEBSOCKET_EXTENSION_CLASS (klass);
-+
-+ extension_class->process_incoming_message = legacy_websocket_extension_process_incoming_message;
-+}
-+
-+static void
-+legacy_websocket_extension_init (LegacyWebsocketExtension *extension)
-+{
-+}
-+
-+static void
-+test_websocket_extension_limit_fallback (void)
-+{
-+ SoupWebsocketExtension *extension;
-+ GBytes *payload;
-+ GBytes *output;
-+ GError *error = NULL;
-+ guint8 header = 0x82;
-+
-+ legacy_extension_processed = FALSE;
-+ extension = g_object_new (LEGACY_TYPE_WEBSOCKET_EXTENSION, NULL);
-+ payload = g_bytes_new_static ("x", 1);
-+ output = soup_websocket_extension_process_incoming_message_with_limit (extension,
-+ &header,
-+ payload,
-+ 1,
-+ &error);
-+
-+ g_assert_no_error (error);
-+ g_assert_true (legacy_extension_processed);
-+ g_assert_true (output == payload);
-+
-+ g_bytes_unref (output);
-+ g_object_unref (extension);
-+}
-+
-+typedef struct {
-+ gsize output_size;
-+ guint64 max_output_size;
-+ gboolean expect_too_big;
-+} DeflateOutputLimitTest;
-+
-+static void
-+test_deflate_output_limit (gconstpointer data)
-+{
-+ const DeflateOutputLimitTest *config = data;
-+ SoupWebsocketExtension *sender;
-+ SoupWebsocketExtension *receiver;
-+ GBytes *compressed;
-+ GBytes *output;
-+ GError *error = NULL;
-+ guint8 header = 0x82;
-+
-+ sender = g_object_new (SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE, NULL);
-+ g_assert_true (soup_websocket_extension_configure (sender,
-+ SOUP_WEBSOCKET_CONNECTION_SERVER,
-+ NULL, &error));
-+ g_assert_no_error (error);
-+
-+ receiver = g_object_new (SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE, NULL);
-+ g_assert_true (soup_websocket_extension_configure (receiver,
-+ SOUP_WEBSOCKET_CONNECTION_CLIENT,
-+ NULL, &error));
-+ g_assert_no_error (error);
-+
-+ compressed = g_bytes_new_take (g_malloc0 (config->output_size), config->output_size);
-+ compressed = soup_websocket_extension_process_outgoing_message (sender, &header,
-+ compressed, &error);
-+ g_assert_no_error (error);
-+ g_assert_nonnull (compressed);
-+ g_assert_true (header & 0x40);
-+ g_assert_cmpuint (g_bytes_get_size (compressed), <, config->output_size);
-+
-+ output = soup_websocket_extension_process_incoming_message_with_limit (receiver,
-+ &header,
-+ compressed,
-+ config->max_output_size,
-+ &error);
-+ if (config->expect_too_big) {
-+ g_assert_null (output);
-+ g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_TOO_BIG);
-+ g_clear_error (&error);
-+ } else {
-+ g_assert_no_error (error);
-+ g_assert_nonnull (output);
-+ g_assert_cmpuint (g_bytes_get_size (output), ==, config->output_size);
-+ g_bytes_unref (output);
-+ }
-+
-+ g_object_unref (receiver);
-+ g_object_unref (sender);
-+}
-+
-+static const DeflateOutputLimitTest deflate_output_below_limit = {
-+ 4095, 4096, FALSE
-+};
-+static const DeflateOutputLimitTest deflate_output_at_limit = {
-+ 4096, 4096, FALSE
-+};
-+static const DeflateOutputLimitTest deflate_output_over_limit = {
-+ 4097, 4096, TRUE
-+};
-+
-+typedef struct {
-+ gboolean server_receives;
-+ gboolean configure_limit;
-+} DeflateMessageLimitTest;
-+
-+static void
-+test_deflate_exceeds_message_limit (Test *test,
-+ gconstpointer data)
-+{
-+ const DeflateMessageLimitTest *config = data;
-+ SoupWebsocketConnection *receiver;
-+ SoupWebsocketConnection *sender;
-+ GBytes *received = NULL;
-+ GError *error = NULL;
-+ guint8 *message;
-+
-+ if (config->server_receives) {
-+ receiver = test->server;
-+ sender = test->client;
-+ } else {
-+ receiver = test->client;
-+ sender = test->server;
-+ }
-+
-+ if (config->configure_limit)
-+ soup_websocket_connection_set_max_total_message_size (receiver, 128 * 1024);
-+
-+ g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (receiver),
-+ ==, 128 * 1024);
-+
-+ g_signal_connect (receiver, "error", G_CALLBACK (on_error_copy_once), &error);
-+ g_signal_connect (receiver, "message", G_CALLBACK (on_binary_message), &received);
-+
-+ message = g_malloc0 (128 * 1024 + 1);
-+ soup_websocket_connection_send_binary (sender, message, 128 * 1024 + 1);
-+ g_free (message);
-+
-+ WAIT_UNTIL (error != NULL || received != NULL);
-+ g_assert_null (received);
-+ g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_TOO_BIG);
-+ g_clear_error (&error);
-+
-+ WAIT_UNTIL (soup_websocket_connection_get_state (sender) == SOUP_WEBSOCKET_STATE_CLOSED);
-+ g_assert_null (received);
-+ g_assert_cmpuint (soup_websocket_connection_get_close_code (sender),
-+ ==, SOUP_WEBSOCKET_CLOSE_TOO_BIG);
-+}
-+
-+static void
-+test_deflate_default_unlimited_message_size (Test *test,
-+ gconstpointer data)
-+{
-+ const DeflateMessageLimitTest *config = data;
-+ SoupWebsocketConnection *receiver;
-+ SoupWebsocketConnection *sender;
-+ GBytes *received = NULL;
-+ guint8 *message;
-+
-+ if (config->server_receives) {
-+ receiver = test->server;
-+ sender = test->client;
-+ } else {
-+ receiver = test->client;
-+ sender = test->server;
-+ }
-+
-+ g_assert_false (config->configure_limit);
-+ g_assert_cmpuint (soup_websocket_connection_get_max_total_message_size (receiver), ==, 0);
-+ g_signal_connect (receiver, "message", G_CALLBACK (on_binary_message), &received);
-+
-+ message = g_malloc0 (128 * 1024 + 1);
-+ soup_websocket_connection_send_binary (sender, message, 128 * 1024 + 1);
-+ g_free (message);
-+
-+ WAIT_UNTIL (received != NULL);
-+ g_assert_cmpuint (g_bytes_get_size (received), ==, 128 * 1024 + 1);
-+ g_bytes_unref (received);
-+}
-+
-+static const DeflateMessageLimitTest deflate_server_receives_configured = { TRUE, TRUE };
-+static const DeflateMessageLimitTest deflate_client_receives_configured = { FALSE, TRUE };
-+static const DeflateMessageLimitTest deflate_server_receives_default = { TRUE, FALSE };
-+static const DeflateMessageLimitTest deflate_client_receives_default = { FALSE, FALSE };
-+
- static void
- do_deflate (z_stream *zstream,
- const char *str,
-@@ -1771,6 +1997,31 @@ test_receive_fragmented (Test *test,
- WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
- }
-
-+static void
-+test_deflate_receive_fragmented_too_big (Test *test,
-+ gconstpointer data)
-+{
-+ GThread *thread;
-+ GBytes *received = NULL;
-+ GError *error = NULL;
-+
-+ soup_websocket_connection_set_max_total_message_size (test->client, 12);
-+ g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy_once), &error);
-+ g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received);
-+
-+ thread = g_thread_new ("deflate-fragment-too-big-thread",
-+ send_compressed_fragments_server_thread,
-+ test);
-+
-+ WAIT_UNTIL (error != NULL || received != NULL);
-+ g_assert_null (received);
-+ g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_TOO_BIG);
-+ g_clear_error (&error);
-+
-+ g_thread_join (thread);
-+ WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
-+}
-+
- typedef struct {
- Test *test;
- const char *header;
-@@ -2798,6 +3049,18 @@ main (int argc,
- test_deflate_negotiate_direct,
- NULL);
-
-+ g_test_add_func ("/websocket/extension/incoming-limit-legacy-fallback",
-+ test_websocket_extension_limit_fallback);
-+ g_test_add_data_func ("/websocket/deflate/output-limit/below",
-+ &deflate_output_below_limit,
-+ test_deflate_output_limit);
-+ g_test_add_data_func ("/websocket/deflate/output-limit/exact",
-+ &deflate_output_at_limit,
-+ test_deflate_output_limit);
-+ g_test_add_data_func ("/websocket/deflate/output-limit/over",
-+ &deflate_output_over_limit,
-+ test_deflate_output_limit);
-+
- g_test_add ("/websocket/direct/deflate-disabled-in-message", Test, NULL, NULL,
- test_deflate_disabled_in_message_direct,
- NULL);
-@@ -2823,6 +3086,37 @@ main (int argc,
- test_send_server_to_client,
- teardown_soup_connection);
-
-+ g_test_add ("/websocket/direct/deflate-configured-limit/client-to-server",
-+ Test, &deflate_server_receives_configured,
-+ setup_direct_connection_with_extensions,
-+ test_deflate_exceeds_message_limit,
-+ teardown_direct_connection);
-+ g_test_add ("/websocket/direct/deflate-configured-limit/server-to-client",
-+ Test, &deflate_client_receives_configured,
-+ setup_direct_connection_with_extensions,
-+ test_deflate_exceeds_message_limit,
-+ teardown_direct_connection);
-+ g_test_add ("/websocket/soup/deflate-default-server-limit/client-to-server",
-+ Test, &deflate_server_receives_default,
-+ setup_soup_connection_with_extensions,
-+ test_deflate_exceeds_message_limit,
-+ teardown_soup_connection);
-+ g_test_add ("/websocket/soup/deflate-configured-client-limit/server-to-client",
-+ Test, &deflate_client_receives_configured,
-+ setup_soup_connection_with_extensions,
-+ test_deflate_exceeds_message_limit,
-+ teardown_soup_connection);
-+ g_test_add ("/websocket/direct/deflate-default-unlimited/client-to-server",
-+ Test, &deflate_server_receives_default,
-+ setup_direct_connection_with_extensions,
-+ test_deflate_default_unlimited_message_size,
-+ teardown_direct_connection);
-+ g_test_add ("/websocket/direct/deflate-default-unlimited/server-to-client",
-+ Test, &deflate_client_receives_default,
-+ setup_direct_connection_with_extensions,
-+ test_deflate_default_unlimited_message_size,
-+ teardown_direct_connection);
-+
- g_test_add ("/websocket/direct/deflate-send-big-packets", Test, NULL,
- setup_direct_connection_with_extensions,
- test_send_big_packets_direct,
-@@ -2845,6 +3139,10 @@ main (int argc,
- setup_half_direct_connection_with_extensions,
- test_receive_fragmented,
- teardown_direct_connection);
-+ g_test_add ("/websocket/direct/deflate-receive-fragmented-too-big", Test, NULL,
-+ setup_half_direct_connection_with_extensions,
-+ test_deflate_receive_fragmented_too_big,
-+ teardown_direct_connection);
- g_test_add ("/websocket/direct/deflate-receive-fragmented-error", Test, NULL,
- setup_half_direct_connection_with_extensions,
- test_deflate_receive_fragmented_error,
---
-GitLab
-
diff --git a/CVE-2026-15711.patch b/CVE-2026-15711.patch
deleted file mode 100644
index 22ab57b..0000000
--- a/CVE-2026-15711.patch
+++ /dev/null
@@ -1,172 +0,0 @@
-From 60aa1ce2bdc7bb5da33be9062f50bcec7db67fca Mon Sep 17 00:00:00 2001
-From: Zayd Rajab <zaydr@amazon.com>
-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 --git a/libsoup/websocket/soup-websocket-connection.c b/libsoup/websocket/soup-websocket-connection.c
-index 14dcd630..ffd713ef 100644
---- a/libsoup/websocket/soup-websocket-connection.c
-+++ b/libsoup/websocket/soup-websocket-connection.c
-@@ -1082,6 +1082,13 @@ process_frame (SoupWebsocketConnection *self)
- 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 --git a/tests/websocket-test.c b/tests/websocket-test.c
-index b8f71300..db088a55 100644
---- a/tests/websocket-test.c
-+++ b/tests/websocket-test.c
-@@ -835,6 +835,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";
-@@ -2574,6 +2666,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);
---
-GitLab
-
diff --git a/CVE-2026-15712.patch b/CVE-2026-15712.patch
deleted file mode 100644
index 5616dec..0000000
--- a/CVE-2026-15712.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From 3a6fb56a0cba42d11f5fd1db6dedcc7c2e92757b Mon Sep 17 00:00:00 2001
-From: Mike Gorse <mgorse@suse.com>
-Date: Fri, 4 Sep 2026 14:05:04 -0500
-Subject: [PATCH] http2: Don't assume that GOAWAY opaque data is NUL-terminated
-
-nghttp2 allocates frame->goaway.opaque_data with exactly opaque_data_len
-bytes and no terminator, so passing it to a "%s" format in h2_debug()
-reads past the end of the buffer when debug logging is enabled.
-
-Use "%.*s" with opaque_data_len as the precision so the read is bounded
-without allocating a copy.
-
-Closes #540
----
- libsoup/http2/soup-client-message-io-http2.c | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
-
-diff --git a/libsoup/http2/soup-client-message-io-http2.c b/libsoup/http2/soup-client-message-io-http2.c
-index ab52003d..b6742924 100644
---- a/libsoup/http2/soup-client-message-io-http2.c
-+++ b/libsoup/http2/soup-client-message-io-http2.c
-@@ -813,10 +813,11 @@ on_frame_recv_callback (nghttp2_session *session,
-
- switch (frame->hd.type) {
- case NGHTTP2_GOAWAY:
-- h2_debug (io, NULL, "[RECV] GOAWAY: error=%s, last_stream_id=%d %s",
-+ h2_debug (io, NULL, "[RECV] GOAWAY: error=%s, last_stream_id=%d %.*s",
- nghttp2_http2_strerror (frame->goaway.error_code),
- frame->goaway.last_stream_id,
-- frame->goaway.opaque_data ? (char *)frame->goaway.opaque_data : "");
-+ (int)frame->goaway.opaque_data_len,
-+ frame->goaway.opaque_data ? (const char *)frame->goaway.opaque_data : "");
- handle_goaway (io, frame->goaway.error_code, frame->goaway.last_stream_id);
- io->is_shutdown = TRUE;
- soup_client_message_io_http2_terminate_session (io);
---
-GitLab
-
diff --git a/CVE-2026-15713.patch b/CVE-2026-15713.patch
deleted file mode 100644
index ef84ebb..0000000
--- a/CVE-2026-15713.patch
+++ /dev/null
@@ -1,148 +0,0 @@
-From 24fb645fa949ece7d7e10363b77cf2d5fa2c2469 Mon Sep 17 00:00:00 2001
-From: Milan Crha <mcrha@redhat.com>
-Date: Fri, 4 Sep 2026 10:34:22 +0200
-Subject: [PATCH] cache: Reject caching authenticated responses in shared
- caches
-
-RFC 7234 Section 3.2 forbids to save to a shared cache a response to
-a request containing an Authorization header unless the response
-explicitly allows it with public, must-revalidate or s-maxage.
-
-This is CVE-2026-15713.
-
-Closes https://gitlab.gnome.org/GNOME/libsoup/-/work_items/541
----
- libsoup/cache/soup-cache.c | 15 +++++++++++
- tests/cache-test.c | 55 ++++++++++++++++++++++++++++++++++++++
- 2 files changed, 70 insertions(+)
-
-diff --git a/libsoup/cache/soup-cache.c b/libsoup/cache/soup-cache.c
-index 47e1ee70..5225382d 100644
---- a/libsoup/cache/soup-cache.c
-+++ b/libsoup/cache/soup-cache.c
-@@ -174,6 +174,7 @@ get_cacheability (SoupCache *cache, SoupMessage *msg)
- SoupCacheability cacheability;
- const char *cache_control, *content_type;
- gboolean has_max_age = FALSE;
-+ gboolean permits_authorized_shared_caching = FALSE;
-
- /* 1. The request method must be cacheable */
- if (soup_message_get_method (msg) == SOUP_METHOD_GET)
-@@ -201,6 +202,11 @@ get_cacheability (SoupCache *cache, SoupMessage *msg)
- soup_header_free_param_list (hash);
- return SOUP_CACHE_UNCACHEABLE;
- }
-+
-+ permits_authorized_shared_caching =
-+ g_hash_table_lookup_extended (hash, "public", NULL, NULL) ||
-+ g_hash_table_lookup_extended (hash, "must-revalidate", NULL, NULL) ||
-+ g_hash_table_lookup_extended (hash, "s-maxage", NULL, NULL);
- }
-
- /* 2. The 'no-store' cache directive does not appear in the
-@@ -225,6 +231,15 @@ get_cacheability (SoupCache *cache, SoupMessage *msg)
- soup_header_free_param_list (hash);
- }
-
-+ /* RFC 7234 Section 3.2: a shared cache MUST NOT store a response to
-+ * a request containing an Authorization header unless the response
-+ * explicitly allows it with public, must-revalidate or s-maxage.
-+ */
-+ if (priv->cache_type == SOUP_CACHE_SHARED &&
-+ !permits_authorized_shared_caching &&
-+ soup_message_headers_get_one_common (soup_message_get_request_headers (msg), SOUP_HEADER_AUTHORIZATION))
-+ return SOUP_CACHE_UNCACHEABLE;
-+
- /* Section 13.9 */
- if ((g_uri_get_query (soup_message_get_uri (msg))) &&
- !soup_message_headers_get_one_common (soup_message_get_response_headers (msg), SOUP_HEADER_EXPIRES) &&
-diff --git a/tests/cache-test.c b/tests/cache-test.c
-index ef2ebeb3..dff6bd35 100644
---- a/tests/cache-test.c
-+++ b/tests/cache-test.c
-@@ -95,6 +95,7 @@ server_callback (SoupServer *server,
- if (status == SOUP_STATUS_OK) {
- GChecksum *sum;
- const char *body;
-+ const char *authorization;
-
- sum = g_checksum_new (G_CHECKSUM_SHA256);
- g_checksum_update (sum, (guchar *)path, strlen (path));
-@@ -102,6 +103,9 @@ server_callback (SoupServer *server,
- g_checksum_update (sum, (guchar *)last_modified, strlen (last_modified));
- if (etag)
- g_checksum_update (sum, (guchar *)etag, strlen (etag));
-+ authorization = soup_message_headers_get_one (request_headers, "Authorization");
-+ if (authorization)
-+ g_checksum_update (sum, (guchar *)authorization, strlen (authorization));
- body = g_checksum_get_string (sum);
- soup_server_message_set_response (msg, "text/plain",
- SOUP_MEMORY_COPY,
-@@ -736,6 +740,56 @@ do_leaks_test (gconstpointer data)
- g_free (cache_dir);
- }
-
-+static void
-+do_shared_cache_authorization_test (gconstpointer data)
-+{
-+ GUri *base_uri = (GUri *)data;
-+ SoupSession *attacker_session;
-+ SoupSession *victim_session;
-+ SoupCache *attacker_cache;
-+ SoupCache *victim_cache;
-+ char *cache_dir;
-+ char *attacker_body;
-+ char *victim_body;
-+
-+ cache_dir = g_dir_make_tmp ("cache-test-XXXXXX", NULL);
-+ debug_printf (2, " Caching to %s\n", cache_dir);
-+
-+ attacker_cache = soup_cache_new (cache_dir, SOUP_CACHE_SHARED);
-+ attacker_session = soup_test_session_new (NULL);
-+ soup_session_add_feature (attacker_session, SOUP_SESSION_FEATURE (attacker_cache));
-+
-+ attacker_body = do_request (attacker_session, base_uri, "GET", "/authz-shared-cache", NULL,
-+ "Authorization", "Bearer ATTACKER_SECRET",
-+ "Test-Set-Cache-Control", "max-age=600",
-+ NULL);
-+ g_assert_true (last_request_hit_network);
-+
-+ soup_cache_dump (attacker_cache);
-+ soup_test_session_abort_unref (attacker_session);
-+ g_object_unref (attacker_cache);
-+
-+ victim_cache = soup_cache_new (cache_dir, SOUP_CACHE_SHARED);
-+ soup_cache_load (victim_cache);
-+ victim_session = soup_test_session_new (NULL);
-+ soup_session_add_feature (victim_session, SOUP_SESSION_FEATURE (victim_cache));
-+
-+ victim_body = do_request (victim_session, base_uri, "GET", "/authz-shared-cache", NULL,
-+ "Authorization", "Bearer VICTIM_SECRET",
-+ "Test-Set-Cache-Control", "max-age=600",
-+ NULL);
-+
-+ g_assert_true (last_request_hit_network);
-+ g_assert_cmpstr (attacker_body, !=, victim_body);
-+
-+ g_free (attacker_body);
-+ g_free (victim_body);
-+
-+ soup_test_session_abort_unref (victim_session);
-+ g_object_unref (victim_cache);
-+ g_free (cache_dir);
-+}
-+
- static void
- do_metrics_test (gconstpointer data)
- {
-@@ -1108,6 +1162,7 @@ main (int argc, char **argv)
- g_test_add_data_func ("/cache/cancellation", base_uri, do_cancel_test);
- g_test_add_data_func ("/cache/refcounting", base_uri, do_refcounting_test);
- g_test_add_data_func ("/cache/headers", base_uri, do_headers_test);
-+ g_test_add_data_func ("/cache/shared-cache-authorization", base_uri, do_shared_cache_authorization_test);
- g_test_add_data_func ("/cache/leaks", base_uri, do_leaks_test);
- g_test_add_data_func ("/cache/metrics", base_uri, do_metrics_test);
- g_test_add_data_func ("/cache/threads", base_uri, do_threads_test);
---
-GitLab
-
diff --git a/CVE-2026-15714.patch b/CVE-2026-15714.patch
deleted file mode 100644
index 451f767..0000000
--- a/CVE-2026-15714.patch
+++ /dev/null
@@ -1,121 +0,0 @@
-From 79a52cadc490360e249cc2b23038d532b44dbf23 Mon Sep 17 00:00:00 2001
-From: Mike Gorse <mgorse@suse.com>
-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(-)
-
-diff --git a/libsoup/soup-multipart-input-stream.c b/libsoup/soup-multipart-input-stream.c
-index b7f1ed72..22cce102 100644
---- a/libsoup/soup-multipart-input-stream.c
-+++ b/libsoup/soup-multipart-input-stream.c
-@@ -361,7 +361,6 @@ soup_multipart_input_stream_read_headers (SoupMultipartInputStream *multipart,
- {
- SoupMultipartInputStreamPrivate *priv = soup_multipart_input_stream_get_instance_private (multipart);
- guchar read_buf[RESPONSE_BLOCK_SIZE];
-- guchar *buf;
- gboolean got_boundary = FALSE;
- gboolean got_lf = FALSE;
- gssize nread = 0;
-@@ -383,22 +382,23 @@ soup_multipart_input_stream_read_headers (SoupMultipartInputStream *multipart,
- * 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) {
-diff --git a/tests/multipart-test.c b/tests/multipart-test.c
-index b1bf5bb8..a598a1c8 100644
---- a/tests/multipart-test.c
-+++ b/tests/multipart-test.c
-@@ -596,6 +596,42 @@ test_multipart_bounds_bad_3 (void)
- g_object_unref (msg);
- }
-
-+static void
-+test_multipart_bounds_bad_4 (void)
-+{
-+ SoupMessage *msg;
-+ SoupMessageHeaders *headers;
-+ 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");
-+ headers = soup_message_get_response_headers (msg);
-+ content_type = g_strconcat ("multipart/form-data; boundary=\"", boundary, "\"", NULL);
-+ soup_message_headers_replace (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_too_large (void)
- {
-@@ -666,6 +702,7 @@ main (int argc, char **argv)
- g_test_add_func ("/multipart/bounds-bad", test_multipart_bounds_bad);
- g_test_add_func ("/multipart/bounds-bad-2", test_multipart_bounds_bad_2);
- g_test_add_func ("/multipart/bounds-bad-3", test_multipart_bounds_bad_3);
-+ g_test_add_func ("/multipart/bounds-bad-4", test_multipart_bounds_bad_4);
- g_test_add_func ("/multipart/too-large", test_multipart_too_large);
-
- ret = g_test_run ();
---
-GitLab
-
diff --git a/CVE-2026-77680.patch b/CVE-2026-77680.patch
deleted file mode 100644
index 2e142b0..0000000
--- a/CVE-2026-77680.patch
+++ /dev/null
@@ -1,681 +0,0 @@
-From e82c13ba03defcee10f981ac964f4d570b21a251 Mon Sep 17 00:00:00 2001
-From: Patrick Griffis <pgriffis@igalia.com>
-Date: Wed, 12 Aug 2026 14:21:52 -0500
-Subject: [PATCH] message-headers: Fix Range parsing overflows and coalescing
- cost
-
-A suffix range longer than the body drove the range start negative, which
-then reached a g_assert() and aborted the process. Clamp it to select the
-whole body instead, as RFC 9110 section 14.1.2 requires. Range starts and
-ends which overflow the goffset they are parsed into are now rejected and
-clamped respectively, rather than wrapping negative and reaching the same
-assertion.
-
-Ranges are no longer coalesced by removing each merged element in turn,
-which was quadratic in their number, and a Range header listing more than
-MAX_RANGES ranges is now answered with 416 instead of being served; RFC
-9110 section 15.5.17 names an excessive number of ranges as a reason for
-that status.
-
-Finally, sort_ranges() returned a goffset difference truncated to int, so
-it reported the wrong order for ranges more than G_MAXINT apart, and the
-merge below it then dropped ranges from responses over 2GB.
-
-Fixes #516
-Fixes #519
-Fixes #535
-Fixes #538
-Fixes #544
-Fixes #547
-Fixes #548
----
- libsoup/soup-message-headers-private.h | 2 +
- libsoup/soup-message-headers.c | 89 +++++--
- tests/range-test.c | 347 +++++++++++++++++++++++++
- tests/server-mem-limit-test.c | 61 +++--
- 4 files changed, 464 insertions(+), 35 deletions(-)
-
-diff --git a/libsoup/soup-message-headers-private.h b/libsoup/soup-message-headers-private.h
-index 708afe98..2e96af34 100644
---- a/libsoup/soup-message-headers-private.h
-+++ b/libsoup/soup-message-headers-private.h
-@@ -12,6 +12,8 @@ G_BEGIN_DECLS
-
- #define MAX_HEADERS_BUFFER_SIZE 256 * 1024 /* 256K */
-
-+#define MAX_RANGES 200
-+
- typedef enum {
- SOUP_HEADER_VALUE_UNTRUSTED,
- SOUP_HEADER_VALUE_TRUSTED
-diff --git a/libsoup/soup-message-headers.c b/libsoup/soup-message-headers.c
-index b2ec036c..7e2e6583 100644
---- a/libsoup/soup-message-headers.c
-+++ b/libsoup/soup-message-headers.c
-@@ -1226,7 +1226,12 @@ sort_ranges (gconstpointer a, gconstpointer b)
- SoupRange *ra = (SoupRange *)a;
- SoupRange *rb = (SoupRange *)b;
-
-- return ra->start - rb->start;
-+ if (ra->start < rb->start)
-+ return -1;
-+ else if (ra->start > rb->start)
-+ return 1;
-+ else
-+ return 0;
- }
-
- /* like soup_message_headers_get_ranges(), except it returns:
-@@ -1270,6 +1275,17 @@ soup_message_headers_get_ranges_internal (SoupMessageHeaders *hdrs,
- if (!range_list)
- return SOUP_STATUS_OK; /* invalid list */
-
-+ /* Reject the header outright if it asks for more ranges than we are
-+ * willing to serve, rather than answering with the whole body: a client
-+ * asking for this many ranges wants to be told so, and RFC 9110 §14.2
-+ * allows rejecting such a header for exactly this reason.
-+ */
-+ if (g_slist_length (range_list) > MAX_RANGES) {
-+ soup_header_free_list (range_list);
-+ return check_satisfiable ? SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE
-+ : SOUP_STATUS_OK;
-+ }
-+
- /* Loop through the ranges and modify the status accordingly. Default to
- * status 200 (OK, ignoring the ranges). Switch to status 206 (Partial
- * Content) if there is at least one partially valid range. Switch to
-@@ -1281,15 +1297,48 @@ soup_message_headers_get_ranges_internal (SoupMessageHeaders *hdrs,
-
- spec = r->data;
- if (*spec == '-') {
-- cur.start = g_ascii_strtoll (spec, &end, 10) + total_length;
-+ gint64 suffix_length;
-+
-+ errno = 0;
-+ suffix_length = g_ascii_strtoll (spec, &end, 10);
-+
-+ /* A suffix range asks for the last -suffix_length bytes
-+ * of the body. If the body is shorter than that then the
-+ * whole body is used, per RFC 9110 §14.1.2; without
-+ * clamping, the start would go negative and reach the
-+ * assertion below.
-+ */
-+ if (errno == ERANGE || suffix_length <= -total_length)
-+ cur.start = 0;
-+ else
-+ cur.start = total_length + suffix_length;
-+
- cur.end = total_length - 1;
- } else {
-- cur.start = g_ascii_strtoull (spec, &end, 10);
-+ guint64 value;
-+
-+ errno = 0;
-+ value = g_ascii_strtoull (spec, &end, 10);
-+ if (errno == ERANGE || value > G_MAXINT64) {
-+ is_all_valid = FALSE;
-+ continue;
-+ }
-+ cur.start = (goffset) value;
-+
- if (*end == '-')
- end++;
-- if (*end)
-- cur.end = g_ascii_strtoull (end, &end, 10);
-- else
-+ if (*end) {
-+ errno = 0;
-+ value = g_ascii_strtoull (end, &end, 10);
-+
-+ /* An end this large is clamped to the end of the
-+ * body below, like any other end past it.
-+ */
-+ if (errno == ERANGE || value > G_MAXINT64)
-+ cur.end = G_MAXINT64;
-+ else
-+ cur.end = (goffset) value;
-+ } else
- cur.end = total_length - 1;
- }
-
-@@ -1330,19 +1379,24 @@ soup_message_headers_get_ranges_internal (SoupMessageHeaders *hdrs,
- }
-
- if (total_length) {
-- guint i;
-+ SoupRange *data;
-+ guint i, last = 0;
-
- g_array_sort (array, sort_ranges);
-- for (i = 1; i < array->len; i++) {
-- SoupRange *cur = &((SoupRange *)array->data)[i];
-- SoupRange *prev = &((SoupRange *)array->data)[i - 1];
-
-- if (cur->start <= prev->end) {
-- prev->end = MAX (prev->end, cur->end);
-- g_array_remove_index (array, i);
-- i--;
-- }
-+ /* Merge overlapping ranges into the run being built at @last.
-+ * Removing the merged elements one at a time instead made this
-+ * quadratic in the number of ranges.
-+ */
-+ data = (SoupRange *)array->data;
-+ for (i = 1; i < array->len; i++) {
-+ if (data[i].start <= data[last].end)
-+ data[last].end = MAX (data[last].end, data[i].end);
-+ else
-+ data[++last] = data[i];
- }
-+
-+ g_array_set_size (array, last + 1);
- }
-
- *ranges = (SoupRange *)array->data;
-@@ -1375,6 +1429,11 @@ soup_message_headers_get_ranges_internal (SoupMessageHeaders *hdrs,
- * Beware that even if given a @total_length, this function does not
- * check that the ranges are satisfiable.
- *
-+ * A Range header requesting more than 200 ranges is rejected, since serving
-+ * that many ranges costs far more than the request asking for them.
-+ * [class@Server] answers such a request with
-+ * %SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE.
-+ *
- * [class@Server] has built-in handling for range requests. If your
- * server handler returns a %SOUP_STATUS_OK response containing the
- * complete response body (rather than pausing the message and
-diff --git a/tests/range-test.c b/tests/range-test.c
-index cfd5f613..9e4ed2c9 100644
---- a/tests/range-test.c
-+++ b/tests/range-test.c
-@@ -3,6 +3,8 @@
- #include "config.h"
-
- #include "test-utils.h"
-+#include "soup-message-headers-private.h"
-+#include "soup-misc.h"
-
- GBytes *full_response;
- int total_length;
-@@ -161,6 +163,21 @@ request_single_range_by_string (SoupSession *session, const char *uri,
- g_object_unref (msg);
- }
-
-+/* Like request_single_range_by_string(), but able to check the ranges of a
-+ * successful 206 as well. */
-+static void
-+request_single_range_by_string_full (SoupSession *session, const char *uri,
-+ const char *range, SoupStatus expected_status,
-+ int expected_start, int expected_end)
-+{
-+ SoupMessage *msg;
-+
-+ msg = soup_message_new ("GET", uri);
-+ soup_message_headers_replace (soup_message_get_request_headers (msg), "Range", range);
-+
-+ do_single_range (session, msg, 0, 0, expected_status, expected_start, expected_end);
-+}
-+
- static void
- do_multi_range (SoupSession *session, SoupMessage *msg,
- int expected_return_ranges)
-@@ -445,6 +462,290 @@ do_range_test (SoupSession *session, const char *uri,
- SOUP_STATUS_OK);
- }
-
-+/* Tests for the Range parser itself. Unlike the tests above, these don't need
-+ * a server, so they can use total lengths which would be impractical to
-+ * actually serve, and they can check the exact status which the server would
-+ * use rather than only the ones a client can distinguish.
-+ */
-+typedef struct {
-+ const char *description;
-+ const char *bugref;
-+ const char *range;
-+ goffset total_length;
-+ guint expected_status;
-+ int expected_n_ranges;
-+ SoupRange expected_ranges[3];
-+} RangeParsingTest;
-+
-+static const RangeParsingTest range_parsing_tests[] = {
-+ /* Valid ranges against a ten byte body, as a baseline. */
-+ { "simple range", NULL,
-+ "bytes=0-4", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 4 } } },
-+ { "whole body", NULL,
-+ "bytes=0-9", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 9 } } },
-+ { "open ended range", NULL,
-+ "bytes=5-", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 5, 9 } } },
-+ { "final byte", NULL,
-+ "bytes=9-", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 9, 9 } } },
-+ { "end past the body is clamped", NULL,
-+ "bytes=1-100", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 1, 9 } } },
-+ { "suffix range", NULL,
-+ "bytes=-5", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 5, 9 } } },
-+ { "single byte suffix range", NULL,
-+ "bytes=-1", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 9, 9 } } },
-+ { "whitespace around the ranges", NULL,
-+ "bytes \t = \t 0-4", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 4 } } },
-+
-+ /* Unsatisfiable and invalid ranges. */
-+ { "start past the body", NULL,
-+ "bytes=10-20", 10, SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE, 0, { } },
-+ { "end before start", NULL,
-+ "bytes=10-1", 10, SOUP_STATUS_OK, 0, { } },
-+ { "zero length suffix range", NULL,
-+ "bytes=-0", 10, SOUP_STATUS_OK, 0, { } },
-+ { "trailing garbage", NULL,
-+ "bytes=0-10 but with weird trailing content", 10, SOUP_STATUS_OK, 0, { } },
-+ { "invalid range dash", NULL,
-+ "bytes=0a10", 10, SOUP_STATUS_OK, 0, { } },
-+ { "unknown range unit", NULL,
-+ "horses=0-10", 10, SOUP_STATUS_OK, 0, { } },
-+ { "missing equals", NULL,
-+ "bytes 0-10", 10, SOUP_STATUS_OK, 0, { } },
-+ { "delimiters but no ranges", NULL,
-+ "bytes=, ,,\t, ", 10, SOUP_STATUS_OK, 0, { } },
-+
-+ /* A suffix length at least as long as the body selects the whole body,
-+ * per RFC 9110 §14.1.2. These used to drive the range start negative,
-+ * which aborted the process at the g_assert() below the parse.
-+ */
-+ { "suffix range the length of the body", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/516",
-+ "bytes=-10", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 9 } } },
-+ { "suffix range one longer than the body", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/516",
-+ "bytes=-11", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 9 } } },
-+ { "suffix range much longer than the body", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/548",
-+ "bytes=-999999", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 9 } } },
-+ { "suffix range of G_MININT", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/547",
-+ "bytes=-2147483648", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 9 } } },
-+ { "suffix range larger than a guint32", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/547",
-+ "bytes=-4294967296", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 9 } } },
-+ { "suffix range of G_MAXINT64", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/547",
-+ "bytes=-9223372036854775807", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 9 } } },
-+ { "suffix range of G_MININT64", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/547",
-+ "bytes=-9223372036854775808", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 9 } } },
-+ { "suffix range overflowing gint64", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/535",
-+ "bytes=-99999999999999999999", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 9 } } },
-+ { "oversized suffix range merged with a valid range", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/548",
-+ "bytes=-999999,4-5", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 9 } } },
-+
-+ /* Range starts and ends which overflow the signed goffset they are
-+ * parsed into. An overflowing start is treated like any other start
-+ * beyond the end of the body, and an overflowing end is clamped like
-+ * any other end beyond the end of the body.
-+ */
-+ { "start overflowing gint64", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/535",
-+ "bytes=9888888888888019900-", 10, SOUP_STATUS_OK, 0, { } },
-+ { "start overflowing gint64 with no dash", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/535",
-+ "bytes=9888888888888019900", 10, SOUP_STATUS_OK, 0, { } },
-+ { "start of G_MAXINT64 + 1", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/535",
-+ "bytes=9223372036854775808-", 10, SOUP_STATUS_OK, 0, { } },
-+ { "start overflowing guint64", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/535",
-+ "bytes=18446744073709551616-", 10, SOUP_STATUS_OK, 0, { } },
-+ { "start and end overflowing gint64", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/535",
-+ "bytes=9888888888888019900-9888888888888019901", 10, SOUP_STATUS_OK, 0, { } },
-+ { "end overflowing gint64", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/535",
-+ "bytes=0-9888888888888019900", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 9 } } },
-+ { "end overflowing guint64", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/535",
-+ "bytes=0-18446744073709551616", 10, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 9 } } },
-+
-+ /* Zero length bodies. soup_message_headers_get_ranges() is public API,
-+ * so it can be called with one even though the server never does.
-+ */
-+ { "range against an empty body", NULL,
-+ "bytes=0-9", 0, SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE, 0, { } },
-+ { "suffix range against an empty body", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/548",
-+ "bytes=-5", 0, SOUP_STATUS_OK, 0, { } },
-+
-+ /* Merging. */
-+ { "overlapping ranges are merged", NULL,
-+ "bytes=0-10,5-20", 100, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 20 } } },
-+ { "contained ranges are merged", NULL,
-+ "bytes=0-20,5-10", 100, SOUP_STATUS_PARTIAL_CONTENT, 1, { { 0, 20 } } },
-+ { "touching ranges are not merged", NULL,
-+ "bytes=0-4,5-9", 100, SOUP_STATUS_PARTIAL_CONTENT, 2, { { 0, 4 }, { 5, 9 } } },
-+ { "ranges are sorted", NULL,
-+ "bytes=20-29,0-9", 100, SOUP_STATUS_PARTIAL_CONTENT, 2, { { 0, 9 }, { 20, 29 } } },
-+ { "invalid ranges do not prevent valid ones", NULL,
-+ "bytes=0-9,50-40,20-29", 100, SOUP_STATUS_PARTIAL_CONTENT, 2, { { 0, 9 }, { 20, 29 } } },
-+
-+ /* The comparison function used to sort the ranges before merging them
-+ * used to truncate a goffset difference to int, which flips its sign
-+ * for bodies over 2GB and silently dropped ranges from the response.
-+ */
-+ { "ranges more than G_MAXINT apart", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/519",
-+ "bytes=0-100, 2500000000-2500000100, 4999999000-4999999100", 5000000000,
-+ SOUP_STATUS_PARTIAL_CONTENT, 3,
-+ { { 0, 100 }, { 2500000000, 2500000100 }, { 4999999000, 4999999100 } } },
-+ { "unsorted ranges more than G_MAXINT apart", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/519",
-+ "bytes=4999999000-4999999100, 0-100, 2500000000-2500000100", 5000000000,
-+ SOUP_STATUS_PARTIAL_CONTENT, 3,
-+ { { 0, 100 }, { 2500000000, 2500000100 }, { 4999999000, 4999999100 } } },
-+};
-+
-+static void
-+check_parsed_ranges (const char *range,
-+ goffset total_length,
-+ guint expected_status,
-+ int expected_n_ranges,
-+ const SoupRange *expected_ranges)
-+{
-+ SoupMessageHeaders *hdrs;
-+ SoupRange *ranges = NULL;
-+ int n_ranges = 0;
-+ guint status;
-+ int i;
-+
-+ hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
-+ soup_message_headers_replace (hdrs, "Range", range);
-+
-+ status = soup_message_headers_get_ranges_internal (hdrs, total_length, TRUE,
-+ &ranges, &n_ranges);
-+
-+ g_assert_cmpuint (status, ==, expected_status);
-+
-+ if (status == SOUP_STATUS_PARTIAL_CONTENT) {
-+ g_assert_nonnull (ranges);
-+ g_assert_cmpint (n_ranges, ==, expected_n_ranges);
-+
-+ for (i = 0; i < n_ranges; i++) {
-+ debug_printf (2, " [%d]: %" G_GINT64_FORMAT "-%" G_GINT64_FORMAT "\n",
-+ i, ranges[i].start, ranges[i].end);
-+
-+ g_assert_cmpint (ranges[i].start, ==, expected_ranges[i].start);
-+ g_assert_cmpint (ranges[i].end, ==, expected_ranges[i].end);
-+
-+ /* Whatever the input, the parsed ranges must be usable
-+ * as offsets into a buffer of total_length bytes.
-+ */
-+ g_assert_cmpint (ranges[i].start, >=, 0);
-+ g_assert_cmpint (ranges[i].end, >=, ranges[i].start);
-+ g_assert_cmpint (ranges[i].end, <, total_length);
-+ }
-+ }
-+
-+ soup_message_headers_free_ranges (hdrs, ranges);
-+ soup_message_headers_unref (hdrs);
-+}
-+
-+static void
-+do_range_parsing_test (void)
-+{
-+ guint i;
-+
-+ for (i = 0; i < G_N_ELEMENTS (range_parsing_tests); i++) {
-+ const RangeParsingTest *test = &range_parsing_tests[i];
-+
-+ debug_printf (1, "%2u. %s: '%s' against %" G_GOFFSET_FORMAT " bytes\n",
-+ i + 1, test->description, test->range, test->total_length);
-+
-+ if (test->bugref)
-+ g_test_message ("Bug reference: %s", test->bugref);
-+
-+ check_parsed_ranges (test->range, test->total_length,
-+ test->expected_status, test->expected_n_ranges,
-+ test->expected_ranges);
-+ }
-+}
-+
-+/* A single Range header can list far more ranges than are reasonable to serve:
-+ * the only limit on the wire is the maximum request header size. */
-+static void
-+do_range_count_test (void)
-+{
-+ struct {
-+ int n_ranges;
-+ gboolean identical;
-+ guint expected_status;
-+ int expected_n_ranges;
-+ } tests[] = {
-+ /* Distinct ranges, up to and then past the limit. Going past it
-+ * is rejected rather than ignored.
-+ */
-+ { 100, FALSE, SOUP_STATUS_PARTIAL_CONTENT, 100 },
-+ { MAX_RANGES, FALSE, SOUP_STATUS_PARTIAL_CONTENT, MAX_RANGES },
-+ { MAX_RANGES + 1, FALSE, SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE, 0 },
-+
-+ /* Identical ranges, which all merge into one. This is the
-+ * shape which used to be quadratic.
-+ */
-+ { MAX_RANGES, TRUE, SOUP_STATUS_PARTIAL_CONTENT, 1 },
-+ { MAX_RANGES + 1, TRUE, SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE, 0 },
-+ { 25585, TRUE, SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE, 0 },
-+ };
-+ guint i;
-+ int j;
-+
-+ for (i = 0; i < G_N_ELEMENTS (tests); i++) {
-+ SoupMessageHeaders *hdrs;
-+ SoupRange *ranges = NULL;
-+ int n_ranges = 0;
-+ guint status;
-+ GString *range;
-+
-+ debug_printf (1, "%2u. %d %s ranges\n", i + 1, tests[i].n_ranges,
-+ tests[i].identical ? "identical" : "distinct");
-+
-+ range = g_string_new ("bytes=");
-+ for (j = 0; j < tests[i].n_ranges; j++) {
-+ int start = tests[i].identical ? 0 : j * 2;
-+
-+ if (j > 0)
-+ g_string_append_c (range, ',');
-+ g_string_append_printf (range, "%d-%d", start, start);
-+ }
-+
-+ hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
-+ soup_message_headers_replace (hdrs, "Range", range->str);
-+ g_string_free (range, TRUE);
-+
-+ status = soup_message_headers_get_ranges_internal (hdrs, 1000000, TRUE,
-+ &ranges, &n_ranges);
-+
-+ g_assert_cmpuint (status, ==, tests[i].expected_status);
-+ if (status == SOUP_STATUS_PARTIAL_CONTENT)
-+ g_assert_cmpint (n_ranges, ==, tests[i].expected_n_ranges);
-+
-+ soup_message_headers_free_ranges (hdrs, ranges);
-+ soup_message_headers_unref (hdrs);
-+ }
-+
-+ /* Callers which don't ask about satisfiability, such as the public
-+ * soup_message_headers_get_ranges(), can't be told 416, so an
-+ * over-limit header just reports no ranges to them.
-+ */
-+ {
-+ SoupMessageHeaders *hdrs;
-+ SoupRange *ranges = NULL;
-+ int n_ranges = 0;
-+ GString *range;
-+
-+ range = g_string_new ("bytes=0-0");
-+ for (j = 0; j < MAX_RANGES; j++)
-+ g_string_append (range, ",0-0");
-+
-+ hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
-+ soup_message_headers_replace (hdrs, "Range", range->str);
-+ g_string_free (range, TRUE);
-+
-+ g_assert_cmpuint (soup_message_headers_get_ranges_internal (hdrs, 1000000, FALSE,
-+ &ranges, &n_ranges),
-+ ==, SOUP_STATUS_OK);
-+ g_assert_false (soup_message_headers_get_ranges (hdrs, 1000000, &ranges, &n_ranges));
-+
-+ soup_message_headers_free_ranges (hdrs, ranges);
-+ soup_message_headers_unref (hdrs);
-+ }
-+}
-+
- #ifdef HAVE_APACHE
- static void
- do_apache_range_test (void)
-@@ -473,6 +774,49 @@ server_handler (SoupServer *server,
- full_response);
- }
-
-+static void
-+do_libsoup_only_range_test (SoupSession *session, const char *uri)
-+{
-+ gsize full_response_length = g_bytes_get_size (full_response);
-+ GString *range;
-+ int i;
-+
-+ /* A suffix length at least as long as the body selects the whole body. */
-+ debug_printf (1, "Requesting (suffix range the length of the body) -%d\n",
-+ (int) full_response_length);
-+ request_single_range (session, uri,
-+ -((int) full_response_length), -1,
-+ SOUP_STATUS_PARTIAL_CONTENT, 0, -1);
-+
-+ debug_printf (1, "Requesting (suffix range longer than the body) -999999\n");
-+ request_single_range_by_string_full (session, uri, "bytes=-999999",
-+ SOUP_STATUS_PARTIAL_CONTENT, 0, -1);
-+
-+ debug_printf (1, "Requesting (suffix range overflowing gint64) -99999999999999999999\n");
-+ request_single_range_by_string_full (session, uri, "bytes=-99999999999999999999",
-+ SOUP_STATUS_PARTIAL_CONTENT, 0, -1);
-+
-+ /* A start which overflows gint64 is treated like any other start past
-+ * the end of the body.
-+ * https://gitlab.gnome.org/GNOME/libsoup/-/issues/535
-+ */
-+ debug_printf (1, "Requesting (start overflowing gint64) 9888888888888019900-\n");
-+ request_single_range_by_string (session, uri, "bytes=9888888888888019900-",
-+ SOUP_STATUS_OK);
-+
-+ /* More ranges than the server is willing to coalesce, which is
-+ * rejected rather than answered with the whole body.
-+ * https://gitlab.gnome.org/GNOME/libsoup/-/issues/538
-+ */
-+ debug_printf (1, "Requesting (more ranges than the limit)\n");
-+ range = g_string_new ("bytes=");
-+ for (i = 0; i < MAX_RANGES + 1; i++)
-+ g_string_append (range, i > 0 ? ",0-0" : "0-0");
-+ request_single_range_by_string (session, uri, range->str,
-+ SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE);
-+ g_string_free (range, TRUE);
-+}
-+
- static void
- do_libsoup_range_test (void)
- {
-@@ -488,6 +832,7 @@ do_libsoup_range_test (void)
- base_uri = soup_test_server_get_uri (server, "http", NULL);
- base_uri_str = g_uri_to_string (base_uri);
- do_range_test (session, base_uri_str, TRUE, TRUE);
-+ do_libsoup_only_range_test (session, base_uri_str);
- g_uri_unref (base_uri);
- g_free (base_uri_str);
- soup_test_server_quit_unref (server);
-@@ -512,6 +857,8 @@ main (int argc, char **argv)
- g_test_add_func ("/ranges/apache", do_apache_range_test);
- #endif
- g_test_add_func ("/ranges/libsoup", do_libsoup_range_test);
-+ g_test_add_func ("/ranges/parsing", do_range_parsing_test);
-+ g_test_add_func ("/ranges/count", do_range_count_test);
-
- ret = g_test_run ();
-
-diff --git a/tests/server-mem-limit-test.c b/tests/server-mem-limit-test.c
-index 65dc875e..ee20ba9b 100644
---- a/tests/server-mem-limit-test.c
-+++ b/tests/server-mem-limit-test.c
-@@ -4,6 +4,7 @@
- */
-
- #include "test-utils.h"
-+#include "soup-message-headers-private.h"
-
- #include <sys/resource.h>
-
-@@ -81,46 +82,66 @@ server_file_callback (SoupServer *server,
- }
-
- static void
--do_ranges_overlaps_test (ServerData *sd, gconstpointer test_data)
-+request_ranges (ServerData *sd, const char *range, SoupStatus expected_status)
- {
- SoupSession *session;
- SoupMessage *msg;
-- GString *range;
- GUri *uri;
-- const char *chunk = ",0,0,0,0,0,0,0,0,0,0,0";
--
-- g_test_bug ("428");
--
-- #ifdef G_OS_WIN32
-- g_test_skip ("Cannot run under windows");
-- return;
-- #endif
--
-- range = g_string_sized_new (99 * 1024);
-- g_string_append (range, "bytes=1024");
-- while (range->len < 99 * 1024)
-- g_string_append (range, chunk);
-
- session = soup_test_session_new (NULL);
-- server_add_handler (sd, "/file", server_file_callback, NULL, NULL);
-
- uri = g_uri_parse_relative (sd->base_uri, "/file", SOUP_HTTP_URI_FLAGS, NULL);
-
- msg = soup_message_new_from_uri ("GET", uri);
-- soup_message_headers_append (soup_message_get_request_headers (msg), "Range", range->str);
-+ soup_message_headers_append (soup_message_get_request_headers (msg), "Range", range);
-
- soup_test_session_send_message (session, msg);
-
-- soup_test_assert_message_status (msg, SOUP_STATUS_PARTIAL_CONTENT);
-+ soup_test_assert_message_status (msg, expected_status);
-
- g_object_unref (msg);
--
-- g_string_free (range, TRUE);
- g_uri_unref (uri);
-
- soup_test_session_abort_unref (session);
- }
-
-+static void
-+do_ranges_overlaps_test (ServerData *sd, gconstpointer test_data)
-+{
-+ GString *range;
-+ const char *chunk = ",0,0,0,0,0,0,0,0,0,0,0";
-+ int i;
-+
-+ g_test_bug ("428");
-+
-+ #ifdef G_OS_WIN32
-+ g_test_skip ("Cannot run under windows");
-+ return;
-+ #endif
-+
-+ server_add_handler (sd, "/file", server_file_callback, NULL, NULL);
-+
-+ /* Requesting the same range many times over used to make the server
-+ * allocate a response proportional to the number of ranges instead of
-+ * coalescing them into one. Each "0" here is an open ended range
-+ * covering the whole body, so they all collapse into a single range.
-+ */
-+ range = g_string_new ("bytes=1024");
-+ for (i = 1; i < MAX_RANGES; i++)
-+ g_string_append (range, ",0");
-+ request_ranges (sd, range->str, SOUP_STATUS_PARTIAL_CONTENT);
-+ g_string_free (range, TRUE);
-+
-+ /* A header listing more ranges than the server is willing to coalesce
-+ * is rejected outright. */
-+ range = g_string_sized_new ((gsize)99 * 1024);
-+ g_string_append (range, "bytes=1024");
-+ while (range->len < (gssize)99 * 1024)
-+ g_string_append (range, chunk);
-+ request_ranges (sd, range->str, SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE);
-+ g_string_free (range, TRUE);
-+}
-+
- int
- main (int argc, char **argv)
- {
---
-GitLab
-
diff --git a/libsoup3.spec b/libsoup3.spec
index ae1bbeb..fcbc341 100644
--- a/libsoup3.spec
+++ b/libsoup3.spec
@@ -7,7 +7,7 @@
%endif
Name: libsoup3
-Version: 3.7.2
+Version: 3.7.3
Release: %autorelease
Summary: Soup, an HTTP library implementation
@@ -21,14 +21,6 @@ Patch: no-ntlm-in-fips-mode.patch
# https://gitlab.gnome.org/GNOME/libsoup/-/work_items/530
Patch: skip-logger-test-on-32bit.patch
-Patch: CVE-2026-77680.patch
-Patch: CVE-2026-15711.patch
-Patch: CVE-2026-15714.patch
-Patch: CVE-2026-15709.patch
-Patch: CVE-2026-15713.patch
-Patch: CVE-2026-15712.patch
-
-
BuildRequires: gcc
BuildRequires: gettext
BuildRequires: glib-networking >= %{glib2_version}
diff --git a/sources b/sources
index d56d967..151acd1 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-SHA512 (libsoup-3.7.2.tar.xz) = 89642fe052557754e151f0d705fa6bca135bc01b1f5e3d2b1f8da67b708a9ffba4b40f923b6a1019a6e2fa28fedb7dc30bed64f98613e4b7139e98dc197fca49
+SHA512 (libsoup-3.7.3.tar.xz) = 44e166742ba649e9b0e6b00d4bcbc80685be8f328a425fcbe1efd960121c33c89b668b19fef2723c93c92af6729d953b5353d133c47d4d18d74886537daa36f6
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-09-24 18:37 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-24 18:37 [rpms/libsoup3] f45: Update to 3.7.3 and drop upstreamed CVE patches Petr Schindler
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox