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-02-24 15:37:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libsoup2 (Old) and /work/SRC/openSUSE:Factory/.libsoup2.new.1977 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libsoup2" Tue Feb 24 15:37:42 2026 rev:21 rq:1334361 version:2.74.3 Changes: -------- --- /work/SRC/openSUSE:Factory/libsoup2/libsoup2.changes 2026-02-16 13:23:33.177629156 +0100 +++ /work/SRC/openSUSE:Factory/.libsoup2.new.1977/libsoup2.changes 2026-02-24 15:37:49.484488351 +0100 @@ -1,0 +2,7 @@ +Thu Feb 19 23:08:22 UTC 2026 - Michael Gorse <[email protected]> + +- Add libsoup2-CVE-2026-2708.patch: do not allow adding multiple + content length values to headers (bsc#1258508 CVE-2026-2708 + glgo#GNOME/libsoup#500). + +------------------------------------------------------------------- New: ---- libsoup2-CVE-2026-2708.patch ----------(New B)---------- New: - Add libsoup2-CVE-2026-2708.patch: do not allow adding multiple content length values to headers (bsc#1258508 CVE-2026-2708 ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libsoup2.spec ++++++ --- /var/tmp/diff_new_pack.xy2yxl/_old 2026-02-24 15:37:50.608534858 +0100 +++ /var/tmp/diff_new_pack.xy2yxl/_new 2026-02-24 15:37:50.612535025 +0100 @@ -96,6 +96,8 @@ Patch34: libsoup2-CVE-2026-2443.patch # PATCH-FIX-UPSTREAM libsoup2-CVE-2026-2369.patch bsc#1258120 [email protected] -- handle potential underflow in the content sniffer. Patch35: libsoup2-CVE-2026-2369.patch +# PATCH-FIX-UPSTREAM libsoup2-CVE-2026-2708.patch bsc#1258508 [email protected] -- do not allow adding multiple content length values to headers. +Patch36: libsoup2-CVE-2026-2708.patch BuildRequires: glib-networking BuildRequires: meson >= 0.50 ++++++ libsoup2-CVE-2026-2708.patch ++++++ >From e032d3e9b0a27d10597398023532dd8f9b6654cf Mon Sep 17 00:00:00 2001 From: Carlos Garcia Campos <[email protected]> Date: Tue, 17 Feb 2026 16:39:26 +0100 Subject: [PATCH] Do not allow adding multiple content length values to headers Closes #500 --- libsoup/soup-message-headers.c | 27 ++++++++++++++ tests/header-parsing-test.c | 50 +++++++++++++++++++++++++- tests/server-test.c | 64 ++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 1 deletion(-) diff -urp libsoup-2.74.3.orig/libsoup/soup-message-headers.c libsoup-2.74.3/libsoup/soup-message-headers.c --- libsoup-2.74.3.orig/libsoup/soup-message-headers.c 2026-02-19 16:18:10.082453707 -0600 +++ libsoup-2.74.3/libsoup/soup-message-headers.c 2026-02-19 18:40:07.082803442 -0600 @@ -206,6 +206,7 @@ soup_message_headers_append_internal (So SoupHeader header; SoupHeaderSetter setter; const char *interned_host = intern_header_name ("Host", NULL); + const char *interned_content_length = intern_header_name ("Content-Length", NULL); g_return_val_if_fail (name != NULL, FALSE); g_return_val_if_fail (value != NULL, FALSE); @@ -234,6 +235,33 @@ soup_message_headers_append_internal (So g_warning ("Attempted to add duplicate Host header to a SoupMessageHeaders that already contains a Host header"); return FALSE; } + if (header.name == interned_content_length) { + /* RFC 9110 - 7.7. Content-Length + * If a message is received that has a Content-Length header field value consisting of + * the same decimal value as a comma-separated list (Section 5.7.1) — for example, + * "Content-Length: 42, 42" — indicating that duplicate Content-Length header fields have + * been generated or combined by an upstream message processor, then the recipient must either + * reject the message as invalid or replace the duplicated field values with a single valid + * Content-Length field containing that decimal value prior to determining the message body + * length or forwarding the message. + */ + const char *content_length = soup_message_headers_get_one (hdrs, "Content-Length"); + if (content_length) { + guint64 decimal_value1, decimal_value2; + char *end; + + decimal_value1 = g_ascii_strtoull (content_length, &end, 10); + if (*end) + return FALSE; + + decimal_value2 = g_ascii_strtoull (value, &end, 10); + if (*end) + return FALSE; + + return (decimal_value1 == decimal_value2); + } + } + header.value = g_strdup (value); g_array_append_val (hdrs->array, header); if (hdrs->concat) diff -urp libsoup-2.74.3.orig/libsoup/soup-message-io.c libsoup-2.74.3/libsoup/soup-message-io.c --- libsoup-2.74.3.orig/libsoup/soup-message-io.c 2026-02-19 16:18:09.780755137 -0600 +++ libsoup-2.74.3/libsoup/soup-message-io.c 2026-02-19 18:39:35.257155442 -0600 @@ -1035,7 +1035,7 @@ io_run_until (SoupMessage *msg, gboolean if (io->read_state == SOUP_MESSAGE_IO_STATE_DONE && io->write_state == SOUP_MESSAGE_IO_STATE_DONE) { SoupURI *uri = soup_message_get_uri (msg); - char *uri_str = soup_uri_to_string (uri, FALSE); + char *uri_str = (uri ? soup_uri_to_string (uri, FALSE) : NULL); const gchar *last_modified = soup_message_headers_get_one (msg->request_headers, "Last-Modified"); const gchar *etag = soup_message_headers_get_one (msg->request_headers, "ETag"); @@ -1049,7 +1049,7 @@ io_run_until (SoupMessage *msg, gboolean "Last-Modified: %s, " "ETag: %s", soup_message_get_https_status (msg, NULL, NULL) ? "HTTPS" : "HTTP", - uri_str, io->read_length, io->write_length, + (uri_str != NULL? uri_str : ""), io->read_length, io->write_length, (last_modified != NULL) ? last_modified : "(unset)", (etag != NULL) ? etag : "(unset)"); g_free (uri_str); diff -urp libsoup-2.74.3.orig/tests/header-parsing-test.c libsoup-2.74.3/tests/header-parsing-test.c --- libsoup-2.74.3.orig/tests/header-parsing-test.c 2026-02-19 16:18:09.973864096 -0600 +++ libsoup-2.74.3/tests/header-parsing-test.c 2026-02-19 17:41:55.553697085 -0600 @@ -367,6 +367,22 @@ static struct RequestTest { } }, + { "Duplicate Content-Length with the same value", NULL, + "POST / HTTP/1.1\r\nContent-Length: 4\r\nContent-Length: 4\r\n", + -1, + SOUP_STATUS_OK, + "POST", "/", SOUP_HTTP_1_1, + { { "Content-Length", "4" } }, 0 + }, + + { "Duplicate Content-Length with the same decimal value", NULL, + "POST / HTTP/1.1\r\nContent-Length: 04\r\nContent-Length: 4\r\n", + -1, + SOUP_STATUS_OK, + "POST", "/", SOUP_HTTP_1_1, + { { "Content-Length", "04" } }, 0 + }, + /************************/ /*** INVALID REQUESTS ***/ /************************/ @@ -468,6 +484,15 @@ static struct RequestTest { SOUP_STATUS_BAD_REQUEST, NULL, NULL, -1, { { NULL } } + }, + + { "Duplicate Content-Length with different value", + "https://gitlab.gnome.org/GNOME/libsoup/-/issues/500", + "POST / HTTP/1.1\r\nContent-Length: 2\r\nContent-Length: 4\r\n", + -1, + SOUP_STATUS_BAD_REQUEST, + NULL, NULL, -1, + { { NULL } }, 0 } }; static const int num_reqtests = G_N_ELEMENTS (reqtests); @@ -1306,6 +1331,28 @@ do_bad_header_tests (void) soup_message_headers_free (hdrs); } +static void +do_append_duplicate_content_length_test (void) +{ + SoupMessageHeaders *hdrs; + const char *list_value; + + hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST); + soup_message_headers_append (hdrs, "Content-Length", "42"); + + /* Inserting the same value doesn't generate a list */ + soup_message_headers_append (hdrs, "Content-Length", "42"); + list_value = soup_message_headers_get_list (hdrs, "Content-Length"); + g_assert_cmpstr (list_value, ==, "42"); + + /* Inserting a different value does nothing */ + soup_message_headers_append (hdrs, "Content-Length", "45"); + list_value = soup_message_headers_get_list (hdrs, "Content-Length"); + g_assert_cmpstr (list_value, ==, "42"); + + soup_message_headers_free (hdrs); +} + int main (int argc, char **argv) { @@ -1321,6 +1368,7 @@ main (int argc, char **argv) g_test_add_func ("/header-parsing/content-type", do_content_type_tests); g_test_add_func ("/header-parsing/append-param", do_append_param_tests); g_test_add_func ("/header-parsing/bad", do_bad_header_tests); + g_test_add_func ("/header-parsing/append-duplicate-content-length", do_append_duplicate_content_length_test); ret = g_test_run (); diff -urp libsoup-2.74.3.orig/tests/server-test.c libsoup-2.74.3/tests/server-test.c --- libsoup-2.74.3.orig/tests/server-test.c 2022-10-11 13:27:22.000000000 -0500 +++ libsoup-2.74.3/tests/server-test.c 2026-02-19 18:42:44.517606492 -0600 @@ -1373,6 +1373,68 @@ do_steal_connect_test (ServerData *sd, g soup_uri_free (proxy_uri); } +static void +do_multiple_content_length_test (ServerData *sd, gconstpointer test_data) +{ + gint i; + struct { + const char *description; + const char *test; + const char *expected_response; + } tests[] = { + { "Double Content-Length with different value", "POST / HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Length: 0\r\nContent-Length: 4\r\nConnection: close\r\n\r\n\r\nABCD", "HTTP/1.1 400 Bad Request" }, + { "Double Content-Length with the same value", "POST / HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Length: 4\r\nContent-Length: 4\r\nConnection: close\r\n\r\n\r\nABCD", "HTTP/1.1 200 OK" }, + }; + + sd->server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); + sd->base_uri = soup_test_server_get_uri (sd->server, "http", NULL); + server_add_handler (sd, NULL, server_callback, NULL, NULL); + + for (i = 0; i < G_N_ELEMENTS (tests); i++) { + GSocketClient *client; + GSocketConnection *conn; + GInputStream *input; + GOutputStream *output; + gsize nwritten; + char buffer[4096]; + gssize nread; + GString *response; + const char *boundary; + GError *error = NULL; + + debug_printf (1, " %s\n", tests[i].description); + + client = g_socket_client_new (); + conn = g_socket_client_connect_to_host (client, soup_uri_get_host (sd->base_uri), soup_uri_get_port (sd->base_uri), NULL, &error); + g_assert_no_error (error); + + output = g_io_stream_get_output_stream (G_IO_STREAM (conn)); + g_output_stream_write_all (output, tests[i].test, strlen (tests[i].test), &nwritten, NULL, &error); + g_assert_no_error (error); + g_assert_cmpuint (nwritten, ==, strlen (tests[i].test)); + g_output_stream_flush (output, NULL, &error); + g_assert_no_error (error); + + response = g_string_new (NULL); + + input = g_io_stream_get_input_stream (G_IO_STREAM (conn)); + do { + nread = g_input_stream_read (input, buffer, sizeof(buffer), NULL, NULL); + if (nread >= 0) + response = g_string_append_len (response, (const char *)buffer, nread); + } while (nread > 0); + + boundary = strstr (response->str, "\r\n"); + g_assert_nonnull (boundary); + response = g_string_truncate (response, response->len - strlen (boundary)); + g_assert_cmpstr (response->str, ==, tests[i].expected_response); + g_string_free (response, TRUE); + + g_object_unref (conn); + g_object_unref (client); + } +} + int main (int argc, char **argv) { @@ -1411,6 +1473,8 @@ main (int argc, char **argv) server_setup_nohandler, do_early_multi_test, server_teardown); g_test_add ("/server/steal/CONNECT", ServerData, NULL, server_setup, do_steal_connect_test, server_teardown); + g_test_add ("/server/multiple-content-length", ServerData, NULL, + NULL, do_multiple_content_length_test, server_teardown); ret = g_test_run ();
