diff --git a/.gitignore b/.gitignore index da50a25..22dba5c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ /libsoup-3.6.1.tar.xz /libsoup-3.6.3.tar.xz /libsoup-3.6.5.tar.xz +/libsoup-3.6.6.tar.xz diff --git a/CVE-2025-12105.patch b/CVE-2025-12105.patch deleted file mode 100644 index fe1142d..0000000 --- a/CVE-2025-12105.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 9ba1243a24e442fa5ec44684617a4480027da960 Mon Sep 17 00:00:00 2001 -From: Eugene Mutavchi -Date: Fri, 10 Oct 2025 16:24:27 +0000 -Subject: [PATCH] fix 'heap-use-after-free' caused by 'finishing' queue item - twice - ---- - libsoup/soup-session.c | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c -index c5694d2c..4e6b478b 100644 ---- a/libsoup/soup-session.c -+++ b/libsoup/soup-session.c -@@ -2894,8 +2894,10 @@ run_until_read_done (SoupMessage *msg, - if (soup_message_io_in_progress (msg)) - soup_message_io_finished (msg); - item->paused = FALSE; -- item->state = SOUP_MESSAGE_FINISHING; -- soup_session_process_queue_item (item->session, item, FALSE); -+ if (item->state != SOUP_MESSAGE_FINISHED) { -+ item->state = SOUP_MESSAGE_FINISHING; -+ soup_session_process_queue_item (item->session, item, FALSE); -+ } - } - async_send_request_return_result (item, NULL, error); - soup_message_queue_item_unref (item); --- -GitLab - diff --git a/CVE-2025-14523.patch b/CVE-2025-14523.patch deleted file mode 100644 index b795345..0000000 --- a/CVE-2025-14523.patch +++ /dev/null @@ -1,695 +0,0 @@ -From 3c975f8d53df061538f865edfe35f9dd4b3e2f80 Mon Sep 17 00:00:00 2001 -From: Michael Catanzaro -Date: Wed, 7 Jan 2026 14:50:33 -0600 -Subject: [PATCH] Reject duplicate Host headers - -RFC 9112 section 3.2 says: - -A server MUST respond with a 400 (Bad Request) status code to any -HTTP/1.1 request message that lacks a Host header field and to any -request message that contains more than one Host header field line or a -Host header field with an invalid field value. - -In addition to rejecting a duplicate header when parsing headers, also -reject attempts to add the duplicate header using the -soup_message_headers_append() API, and add tests for both cases. - -These checks will also apply to HTTP/2. I'm not sure whether this is -actually desired or not, but the header processing code is not aware of -which HTTP version is in use. - -(Note that while SoupMessageHeaders does not require the Host header to -be present in an HTTP/1.1 request, SoupServer itself does. So we can't -test the case of missing Host header via the header parsing test, but it -really is enforced.) - -Fixes #472 ---- - libsoup/soup-headers.c | 3 +- - libsoup/soup-message-headers-private.h | 4 +- - libsoup/soup-message-headers.c | 78 +++++++++------ - tests/header-parsing-test.c | 133 ++++++++++++++++--------- - 4 files changed, 137 insertions(+), 81 deletions(-) - -diff --git a/libsoup/soup-headers.c b/libsoup/soup-headers.c -index 52ef2ece..9dcffbf2 100644 ---- a/libsoup/soup-headers.c -+++ b/libsoup/soup-headers.c -@@ -139,7 +139,8 @@ soup_headers_parse (const char *str, int len, SoupMessageHeaders *dest) - for (p = strchr (value, '\r'); p; p = strchr (p, '\r')) - *p = ' '; - -- soup_message_headers_append_untrusted_data (dest, name, value); -+ if (!soup_message_headers_append_untrusted_data (dest, name, value)) -+ goto done; - } - success = TRUE; - -diff --git a/libsoup/soup-message-headers-private.h b/libsoup/soup-message-headers-private.h -index 98154645..770f3ef1 100644 ---- a/libsoup/soup-message-headers-private.h -+++ b/libsoup/soup-message-headers-private.h -@@ -10,10 +10,10 @@ - - G_BEGIN_DECLS - --void soup_message_headers_append_untrusted_data (SoupMessageHeaders *hdrs, -+gboolean soup_message_headers_append_untrusted_data (SoupMessageHeaders *hdrs, - const char *name, - const char *value); --void soup_message_headers_append_common (SoupMessageHeaders *hdrs, -+gboolean soup_message_headers_append_common (SoupMessageHeaders *hdrs, - SoupHeaderName name, - const char *value); - const char *soup_message_headers_get_one_common (SoupMessageHeaders *hdrs, -diff --git a/libsoup/soup-message-headers.c b/libsoup/soup-message-headers.c -index f101d4b4..b0d5828f 100644 ---- a/libsoup/soup-message-headers.c -+++ b/libsoup/soup-message-headers.c -@@ -273,12 +273,16 @@ soup_message_headers_clean_connection_headers (SoupMessageHeaders *hdrs) - soup_header_free_list (tokens); - } - --void -+gboolean - soup_message_headers_append_common (SoupMessageHeaders *hdrs, - SoupHeaderName name, - const char *value) - { - SoupCommonHeader header; -+ if (name == SOUP_HEADER_HOST && soup_message_headers_get_one (hdrs, "Host")) { -+ g_warning ("Attempted to add duplicate Host header to a SoupMessageHeaders that already contains a Host header"); -+ return FALSE; -+ } - - if (!hdrs->common_headers) - hdrs->common_headers = g_array_sized_new (FALSE, FALSE, sizeof (SoupCommonHeader), 6); -@@ -290,33 +294,19 @@ soup_message_headers_append_common (SoupMessageHeaders *hdrs, - g_hash_table_remove (hdrs->common_concat, GUINT_TO_POINTER (header.name)); - - soup_message_headers_set (hdrs, name, value); -+ return TRUE; - } - --/** -- * soup_message_headers_append: -- * @hdrs: a #SoupMessageHeaders -- * @name: the header name to add -- * @value: the new value of @name -- * -- * Appends a new header with name @name and value @value to @hdrs. -- * -- * (If there is an existing header with name @name, then this creates a second -- * one, which is only allowed for list-valued headers; see also -- * [method@MessageHeaders.replace].) -- * -- * The caller is expected to make sure that @name and @value are -- * syntactically correct. -- **/ --void --soup_message_headers_append (SoupMessageHeaders *hdrs, -- const char *name, const char *value) -+static gboolean -+soup_message_headers_append_internal (SoupMessageHeaders *hdrs, -+ const char *name, const char *value) - { - SoupUncommonHeader header; - SoupHeaderName header_name; - -- g_return_if_fail (hdrs); -- g_return_if_fail (name != NULL); -- g_return_if_fail (value != NULL); -+ g_return_val_if_fail (hdrs, FALSE); -+ g_return_val_if_fail (name != NULL, FALSE); -+ g_return_val_if_fail (value != NULL, FALSE); - - /* Setting a syntactically invalid header name or value is - * considered to be a programming error. However, it can also -@@ -324,23 +314,22 @@ soup_message_headers_append (SoupMessageHeaders *hdrs, - * compiled with G_DISABLE_CHECKS. - */ - #ifndef G_DISABLE_CHECKS -- g_return_if_fail (*name && strpbrk (name, " \t\r\n:") == NULL); -- g_return_if_fail (strpbrk (value, "\r\n") == NULL); -+ g_return_val_if_fail (*name && strpbrk (name, " \t\r\n:") == NULL, FALSE); -+ g_return_val_if_fail (strpbrk (value, "\r\n") == NULL, FALSE); - #else - if (*name && strpbrk (name, " \t\r\n:")) { - g_warning ("soup_message_headers_append: Ignoring bad name '%s'", name); -- return; -+ return FALSE; - } - if (strpbrk (value, "\r\n")) { - g_warning ("soup_message_headers_append: Ignoring bad value '%s'", value); -- return; -+ return FALSE; - } - #endif - - header_name = soup_header_name_from_string (name); - if (header_name != SOUP_HEADER_UNKNOWN) { -- soup_message_headers_append_common (hdrs, header_name, value); -- return; -+ return soup_message_headers_append_common (hdrs, header_name, value); - } - - if (!hdrs->uncommon_headers) -@@ -351,21 +340,48 @@ soup_message_headers_append (SoupMessageHeaders *hdrs, - g_array_append_val (hdrs->uncommon_headers, header); - if (hdrs->uncommon_concat) - g_hash_table_remove (hdrs->uncommon_concat, header.name); -+ return TRUE; -+} -+ -+/** -+ * soup_message_headers_append: -+ * @hdrs: a #SoupMessageHeaders -+ * @name: the header name to add -+ * @value: the new value of @name -+ * -+ * Appends a new header with name @name and value @value to @hdrs. -+ * -+ * (If there is an existing header with name @name, then this creates a second -+ * one, which is only allowed for list-valued headers; see also -+ * [method@MessageHeaders.replace].) -+ * -+ * The caller is expected to make sure that @name and @value are -+ * syntactically correct. -+ **/ -+void -+soup_message_headers_append (SoupMessageHeaders *hdrs, -+ const char *name, const char *value) -+{ -+ soup_message_headers_append_internal (hdrs, name, value); - } - - /* -- * Appends a header value ensuring that it is valid UTF8. -+ * Appends a header value ensuring that it is valid UTF-8, and also checking the -+ * return value of soup_message_headers_append_internal() to report whether the -+ * headers are invalid for various other reasons. - */ --void -+gboolean - soup_message_headers_append_untrusted_data (SoupMessageHeaders *hdrs, - const char *name, - const char *value) - { - char *safe_value = g_utf8_make_valid (value, -1); - char *safe_name = g_utf8_make_valid (name, -1); -- soup_message_headers_append (hdrs, safe_name, safe_value); -+ gboolean result = soup_message_headers_append_internal (hdrs, safe_name, safe_value); -+ - g_free (safe_value); - g_free (safe_name); -+ return result; - } - - void -diff --git a/tests/header-parsing-test.c b/tests/header-parsing-test.c -index 4faafbd6..ea42d5e6 100644 ---- a/tests/header-parsing-test.c -+++ b/tests/header-parsing-test.c -@@ -24,6 +24,7 @@ static struct RequestTest { - const char *method, *path; - SoupHTTPVersion version; - Header headers[10]; -+ GLogLevelFlags log_flags; - } reqtests[] = { - /**********************/ - /*** VALID REQUESTS ***/ -@@ -33,7 +34,7 @@ static struct RequestTest { - "GET / HTTP/1.0\r\n", -1, - SOUP_STATUS_OK, - "GET", "/", SOUP_HTTP_1_0, -- { { NULL } } -+ { { NULL } }, 0 - }, - - { "Req w/ 1 header", NULL, -@@ -42,7 +43,7 @@ static struct RequestTest { - "GET", "/", SOUP_HTTP_1_1, - { { "Host", "example.com" }, - { NULL } -- } -+ }, 0 - }, - - { "Req w/ 1 header, no leading whitespace", NULL, -@@ -51,7 +52,7 @@ static struct RequestTest { - "GET", "/", SOUP_HTTP_1_1, - { { "Host", "example.com" }, - { NULL } -- } -+ }, 0 - }, - - { "Req w/ 1 header including trailing whitespace", NULL, -@@ -60,7 +61,7 @@ static struct RequestTest { - "GET", "/", SOUP_HTTP_1_1, - { { "Host", "example.com" }, - { NULL } -- } -+ }, 0 - }, - - { "Req w/ 1 header, wrapped", NULL, -@@ -69,7 +70,7 @@ static struct RequestTest { - "GET", "/", SOUP_HTTP_1_1, - { { "Foo", "bar baz" }, - { NULL } -- } -+ }, 0 - }, - - { "Req w/ 1 header, wrapped with additional whitespace", NULL, -@@ -78,7 +79,7 @@ static struct RequestTest { - "GET", "/", SOUP_HTTP_1_1, - { { "Foo", "bar baz" }, - { NULL } -- } -+ }, 0 - }, - - { "Req w/ 1 header, wrapped with tab", NULL, -@@ -87,7 +88,7 @@ static struct RequestTest { - "GET", "/", SOUP_HTTP_1_1, - { { "Foo", "bar baz" }, - { NULL } -- } -+ }, 0 - }, - - { "Req w/ 1 header, wrapped before value", NULL, -@@ -96,7 +97,7 @@ static struct RequestTest { - "GET", "/", SOUP_HTTP_1_1, - { { "Foo", "bar baz" }, - { NULL } -- } -+ }, 0 - }, - - { "Req w/ 1 header with empty value", NULL, -@@ -105,7 +106,7 @@ static struct RequestTest { - "GET", "/", SOUP_HTTP_1_1, - { { "Host", "" }, - { NULL } -- } -+ }, 0 - }, - - { "Req w/ 2 headers", NULL, -@@ -115,7 +116,7 @@ static struct RequestTest { - { { "Host", "example.com" }, - { "Connection", "close" }, - { NULL } -- } -+ }, 0 - }, - - { "Req w/ 3 headers", NULL, -@@ -126,7 +127,7 @@ static struct RequestTest { - { "Connection", "close" }, - { "Blah", "blah" }, - { NULL } -- } -+ }, 0 - }, - - { "Req w/ 3 headers, 1st wrapped", NULL, -@@ -137,7 +138,7 @@ static struct RequestTest { - { "Foo", "bar baz" }, - { "Blah", "blah" }, - { NULL } -- } -+ }, 0 - }, - - { "Req w/ 3 headers, 2nd wrapped", NULL, -@@ -148,7 +149,7 @@ static struct RequestTest { - { "Blah", "blah" }, - { "Foo", "bar baz" }, - { NULL } -- } -+ }, 0 - }, - - { "Req w/ 3 headers, 3rd wrapped", NULL, -@@ -159,7 +160,7 @@ static struct RequestTest { - { "Blah", "blah" }, - { "Foo", "bar baz" }, - { NULL } -- } -+ }, 0 - }, - - { "Req w/ same header multiple times", NULL, -@@ -168,7 +169,7 @@ static struct RequestTest { - "GET", "/", SOUP_HTTP_1_1, - { { "Foo", "bar, baz, quux" }, - { NULL } -- } -+ }, 0 - }, - - { "Connection header on HTTP/1.0 message", NULL, -@@ -178,21 +179,21 @@ static struct RequestTest { - { { "Connection", "Bar, Quux" }, - { "Foo", "bar" }, - { NULL } -- } -+ }, 0 - }, - - { "GET with full URI", "667637", - "GET http://example.com HTTP/1.1\r\n", -1, - SOUP_STATUS_OK, - "GET", "http://example.com", SOUP_HTTP_1_1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - { "GET with full URI in upper-case", "667637", - "GET HTTP://example.com HTTP/1.1\r\n", -1, - SOUP_STATUS_OK, - "GET", "HTTP://example.com", SOUP_HTTP_1_1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - /* It's better for this to be passed through: this means a SoupServer -@@ -202,7 +203,7 @@ static struct RequestTest { - "GET AbOuT: HTTP/1.1\r\n", -1, - SOUP_STATUS_OK, - "GET", "AbOuT:", SOUP_HTTP_1_1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - /****************************/ -@@ -217,7 +218,7 @@ static struct RequestTest { - "GET", "/", SOUP_HTTP_1_1, - { { "Host", "example.com" }, - { NULL } -- } -+ }, 0 - }, - - /* RFC 2616 section 3.1 says we MUST accept this */ -@@ -228,7 +229,7 @@ static struct RequestTest { - "GET", "/", SOUP_HTTP_1_1, - { { "Host", "example.com" }, - { NULL } -- } -+ }, 0 - }, - - /* RFC 2616 section 19.3 says we SHOULD accept these */ -@@ -240,7 +241,7 @@ static struct RequestTest { - { { "Host", "example.com" }, - { "Connection", "close" }, - { NULL } -- } -+ }, 0 - }, - - { "LF instead of CRLF after Request-Line", NULL, -@@ -249,7 +250,7 @@ static struct RequestTest { - "GET", "/", SOUP_HTTP_1_1, - { { "Host", "example.com" }, - { NULL } -- } -+ }, 0 - }, - - { "Mixed CRLF/LF", "666316", -@@ -261,7 +262,7 @@ static struct RequestTest { - { "e", "f" }, - { "g", "h" }, - { NULL } -- } -+ }, 0 - }, - - { "Req w/ incorrect whitespace in Request-Line", NULL, -@@ -270,7 +271,7 @@ static struct RequestTest { - "GET", "/", SOUP_HTTP_1_1, - { { "Host", "example.com" }, - { NULL } -- } -+ }, 0 - }, - - { "Req w/ incorrect whitespace after Request-Line", "475169", -@@ -279,7 +280,7 @@ static struct RequestTest { - "GET", "/", SOUP_HTTP_1_1, - { { "Host", "example.com" }, - { NULL } -- } -+ }, 0 - }, - - /* If the request/status line is parseable, then we -@@ -293,7 +294,7 @@ static struct RequestTest { - { { "Host", "example.com" }, - { "Bar", "two" }, - { NULL } -- } -+ }, 0 - }, - - { "First header line is continuation", "666316", -@@ -303,7 +304,7 @@ static struct RequestTest { - { { "Host", "example.com" }, - { "c", "d" }, - { NULL } -- } -+ }, 0 - }, - - { "Zero-length header name", "666316", -@@ -313,7 +314,7 @@ static struct RequestTest { - { { "a", "b" }, - { "c", "d" }, - { NULL } -- } -+ }, 0 - }, - - { "CR in header name", "666316", -@@ -323,7 +324,7 @@ static struct RequestTest { - { { "a", "b" }, - { "c", "d" }, - { NULL } -- } -+ }, 0 - }, - - { "CR in header value", "666316", -@@ -336,7 +337,7 @@ static struct RequestTest { - { "s", "t" }, /* CR at end is ignored */ - { "c", "d" }, - { NULL } -- } -+ }, 0 - }, - - { "Tab in header name", "666316", -@@ -351,7 +352,7 @@ static struct RequestTest { - { "p", "q z: w" }, - { "c", "d" }, - { NULL } -- } -+ }, 0 - }, - - { "Tab in header value", "666316", -@@ -364,7 +365,7 @@ static struct RequestTest { - { "z", "w" }, /* trailing tab ignored */ - { "c", "d" }, - { NULL } -- } -+ }, 0 - }, - - /************************/ -@@ -375,77 +376,77 @@ static struct RequestTest { - "GET /\r\n", -1, - SOUP_STATUS_BAD_REQUEST, - NULL, NULL, -1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - { "HTTP 1.2 request (no such thing)", NULL, - "GET / HTTP/1.2\r\n", -1, - SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED, - NULL, NULL, -1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - { "HTTP 2000 request (no such thing)", NULL, - "GET / HTTP/2000.0\r\n", -1, - SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED, - NULL, NULL, -1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - { "Long HTTP version terminating at missing minor version", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/404", - unterminated_http_version, sizeof (unterminated_http_version), - SOUP_STATUS_BAD_REQUEST, - NULL, NULL, -1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - { "Non-HTTP request", NULL, - "GET / SOUP/1.1\r\nHost: example.com\r\n", -1, - SOUP_STATUS_BAD_REQUEST, - NULL, NULL, -1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - { "Junk after Request-Line", NULL, - "GET / HTTP/1.1 blah\r\nHost: example.com\r\n", -1, - SOUP_STATUS_BAD_REQUEST, - NULL, NULL, -1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - { "NUL in Method", NULL, - "G\x00T / HTTP/1.1\r\nHost: example.com\r\n", 37, - SOUP_STATUS_BAD_REQUEST, - NULL, NULL, -1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - { "NUL at beginning of Method", "666316", - "\x00 / HTTP/1.1\r\nHost: example.com\r\n", 35, - SOUP_STATUS_BAD_REQUEST, - NULL, NULL, -1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - { "NUL in Path", NULL, - "GET /\x00 HTTP/1.1\r\nHost: example.com\r\n", 38, - SOUP_STATUS_BAD_REQUEST, - NULL, NULL, -1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - { "No terminating CRLF", NULL, - "GET / HTTP/1.1\r\nHost: example.com", -1, - SOUP_STATUS_BAD_REQUEST, - NULL, NULL, -1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - { "Unrecognized expectation", NULL, - "GET / HTTP/1.1\r\nHost: example.com\r\nExpect: the-impossible\r\n", -1, - SOUP_STATUS_EXPECTATION_FAILED, - NULL, NULL, -1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - // https://gitlab.gnome.org/GNOME/libsoup/-/issues/377 -@@ -453,21 +454,31 @@ static struct RequestTest { - "GET / HTTP/1.1\r\nHost\x00: example.com\r\n", 36, - SOUP_STATUS_BAD_REQUEST, - NULL, NULL, -1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - { "NUL in header value", NULL, - "HTTP/1.1 200 OK\r\nFoo: b\x00" "ar\r\n", 28, - SOUP_STATUS_BAD_REQUEST, - NULL, NULL, -1, -- { { NULL } } -+ { { NULL } }, 0 - }, - - { "Only newlines", NULL, - only_newlines, sizeof (only_newlines), - SOUP_STATUS_BAD_REQUEST, - NULL, NULL, -1, -- { { NULL } } -+ { { NULL } }, 0 -+ }, -+ -+ { "Duplicate Host headers", -+ "https://gitlab.gnome.org/GNOME/libsoup/-/issues/472", -+ "GET / HTTP/1.1\r\nHost: example.com\r\nHost: example.org\r\n", -+ -1, -+ SOUP_STATUS_BAD_REQUEST, -+ NULL, NULL, -1, -+ { { NULL } }, -+ G_LOG_LEVEL_WARNING - } - }; - static const int num_reqtests = G_N_ELEMENTS (reqtests); -@@ -915,10 +926,17 @@ do_request_tests (void) - len = strlen (reqtests[i].request); - else - len = reqtests[i].length; -+ -+ if (reqtests[i].log_flags) -+ g_test_expect_message ("libsoup", reqtests[i].log_flags, "*"); -+ - status = soup_headers_parse_request (reqtests[i].request, len, - headers, &method, &path, - &version); - g_assert_cmpint (status, ==, reqtests[i].status); -+ if (reqtests[i].log_flags) -+ g_test_assert_expected_messages (); -+ - if (SOUP_STATUS_IS_SUCCESSFUL (status)) { - g_assert_cmpstr (method, ==, reqtests[i].method); - g_assert_cmpstr (path, ==, reqtests[i].path); -@@ -1314,6 +1332,25 @@ do_bad_header_tests (void) - soup_message_headers_unref (hdrs); - } - -+static void -+do_append_duplicate_host_test (void) -+{ -+ SoupMessageHeaders *hdrs; -+ const char *list_value; -+ -+ hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST); -+ soup_message_headers_append (hdrs, "Host", "a"); -+ g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, -+ "Attempted to add duplicate Host header to a SoupMessageHeaders that already contains a Host header"); -+ soup_message_headers_append (hdrs, "Host", "b"); -+ g_test_assert_expected_messages (); -+ -+ list_value = soup_message_headers_get_list (hdrs, "Host"); -+ g_assert_cmpstr (list_value, ==, "a"); -+ -+ soup_message_headers_unref (hdrs); -+} -+ - int - main (int argc, char **argv) - { -@@ -1330,6 +1367,8 @@ main (int argc, char **argv) - 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-host", do_append_duplicate_host_test); -+ - ret = g_test_run (); - - test_cleanup (); --- -2.52.0 - diff --git a/CVE-2025-32907.patch b/CVE-2025-32907.patch deleted file mode 100644 index 41ec1e5..0000000 --- a/CVE-2025-32907.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 84e601252a9ae5eafaba9cb9cb5e4bd77ca41bdb Mon Sep 17 00:00:00 2001 -From: Milan Crha -Date: Tue, 15 Apr 2025 12:17:39 +0200 -Subject: [PATCH] soup-message-headers: Correct merge of ranges - -It had been skipping every second range, which generated an array -of a lot of insane ranges, causing large memory usage by the server. - -Closes #428 ---- - libsoup/soup-message-headers.c | 1 + - tests/meson.build | 1 + - tests/server-mem-limit-test.c | 144 +++++++++++++++++++++++++++++++++ - 3 files changed, 146 insertions(+) - create mode 100644 tests/server-mem-limit-test.c - -diff --git a/libsoup/soup-message-headers.c b/libsoup/soup-message-headers.c -index 64847e30..f612bff1 100644 ---- a/libsoup/soup-message-headers.c -+++ b/libsoup/soup-message-headers.c -@@ -1024,6 +1024,7 @@ soup_message_headers_get_ranges_internal (SoupMessageHeaders *hdrs, - if (cur->start <= prev->end) { - prev->end = MAX (prev->end, cur->end); - g_array_remove_index (array, i); -+ i--; - } - } - } --- -2.49.0 diff --git a/CVE-2025-32908.patch b/CVE-2025-32908.patch deleted file mode 100644 index 4b6bcb6..0000000 --- a/CVE-2025-32908.patch +++ /dev/null @@ -1,130 +0,0 @@ -From a792b23ab87cacbf4dd9462bf7b675fa678efbae Mon Sep 17 00:00:00 2001 -From: Milan Crha -Date: Tue, 15 Apr 2025 09:59:05 +0200 -Subject: [PATCH] soup-server-http2: Check validity of the constructed - connection URI - -The HTTP/2 pseudo-headers can contain invalid values, which the GUri rejects -and returns NULL, but the soup-server did not check the validity and could -abort the server itself later in the code. - -Closes #429 ---- - .../http2/soup-server-message-io-http2.c | 4 +++ - tests/http2-test.c | 28 +++++++++++++++++++ - 2 files changed, 32 insertions(+) - -diff --git a/libsoup/server/http2/soup-server-message-io-http2.c b/libsoup/server/http2/soup-server-message-io-http2.c -index 943ecfd3..f1fe2d5c 100644 ---- a/libsoup/server/http2/soup-server-message-io-http2.c -+++ b/libsoup/server/http2/soup-server-message-io-http2.c -@@ -771,9 +771,13 @@ on_frame_recv_callback (nghttp2_session *session, - char *uri_string; - GUri *uri; - -+ if (msg_io->scheme == NULL || msg_io->authority == NULL || msg_io->path == NULL) -+ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - uri_string = g_strdup_printf ("%s://%s%s", msg_io->scheme, msg_io->authority, msg_io->path); - uri = g_uri_parse (uri_string, SOUP_HTTP_URI_FLAGS, NULL); - g_free (uri_string); -+ if (uri == NULL) -+ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - soup_server_message_set_uri (msg_io->msg, uri); - g_uri_unref (uri); - -diff --git a/tests/http2-test.c b/tests/http2-test.c -index 5b6da5e4..ec7972fe 100644 ---- a/tests/http2-test.c -+++ b/tests/http2-test.c -@@ -1341,6 +1341,30 @@ do_connection_closed_test (Test *test, gconstpointer data) - g_uri_unref (uri); - } - -+static void -+do_broken_pseudo_header_test (Test *test, gconstpointer data) -+{ -+ char *path; -+ SoupMessage *msg; -+ GUri *uri; -+ GBytes *body = NULL; -+ GError *error = NULL; -+ -+ uri = g_uri_parse_relative (base_uri, "/ag", SOUP_HTTP_URI_FLAGS, NULL); -+ -+ /* an ugly cheat to construct a broken URI, which can be sent from other libs */ -+ path = (char *) g_uri_get_path (uri); -+ path[1] = '%'; -+ -+ msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); -+ body = soup_test_session_async_send (test->session, msg, NULL, &error); -+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT); -+ g_assert_null (body); -+ g_clear_error (&error); -+ g_object_unref (msg); -+ g_uri_unref (uri); -+} -+ - static gboolean - unpause_message (SoupServerMessage *msg) - { -@@ -1662,6 +1686,10 @@ main (int argc, char **argv) - setup_session, - do_connection_closed_test, - teardown_session); -+ g_test_add ("/http2/broken-pseudo-header", Test, NULL, -+ setup_session, -+ do_broken_pseudo_header_test, -+ teardown_session); - - ret = g_test_run (); - --- -GitLab - -From 527428a033df573ef4558ce1106e080fd9ec5c71 Mon Sep 17 00:00:00 2001 -From: Milan Crha -Date: Mon, 28 Apr 2025 10:55:42 +0200 -Subject: [PATCH] soup-server-http2: Correct check of the validity of the - constructed connection URI - -RFC 5740: the CONNECT has unset the "scheme" and "path", thus allow them unset. - -The commit a792b23ab87cacbf4dd9462bf7b675fa678efbae also missed to decrement -the `io->in_callback` in the early returns. - -Related to #429 ---- - .../server/http2/soup-server-message-io-http2.c | 15 ++++++++++----- - 1 file changed, 10 insertions(+), 5 deletions(-) - -diff --git a/libsoup/server/http2/soup-server-message-io-http2.c b/libsoup/server/http2/soup-server-message-io-http2.c -index f1fe2d5c..913afb46 100644 ---- a/libsoup/server/http2/soup-server-message-io-http2.c -+++ b/libsoup/server/http2/soup-server-message-io-http2.c -@@ -771,13 +771,18 @@ on_frame_recv_callback (nghttp2_session *session, - char *uri_string; - GUri *uri; - -- if (msg_io->scheme == NULL || msg_io->authority == NULL || msg_io->path == NULL) -- return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; -- uri_string = g_strdup_printf ("%s://%s%s", msg_io->scheme, msg_io->authority, msg_io->path); -+ if (msg_io->authority == NULL) { -+ io->in_callback--; -+ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; -+ } -+ /* RFC 5740: the CONNECT has unset the "scheme" and "path", but the GUri requires the scheme, thus let it be "(null)" */ -+ uri_string = g_strdup_printf ("%s://%s%s", msg_io->scheme, msg_io->authority, msg_io->path == NULL ? "" : msg_io->path); - uri = g_uri_parse (uri_string, SOUP_HTTP_URI_FLAGS, NULL); - g_free (uri_string); -- if (uri == NULL) -- return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; -+ if (uri == NULL) { -+ io->in_callback--; -+ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; -+ } - soup_server_message_set_uri (msg_io->msg, uri); - g_uri_unref (uri); - --- -GitLab - diff --git a/CVE-2025-32914.patch b/CVE-2025-32914.patch deleted file mode 100644 index 5ec88a9..0000000 --- a/CVE-2025-32914.patch +++ /dev/null @@ -1,107 +0,0 @@ -From 5bfcf8157597f2d327050114fb37ff600004dbcf Mon Sep 17 00:00:00 2001 -From: Milan Crha -Date: Tue, 15 Apr 2025 09:03:00 +0200 -Subject: [PATCH] multipart: Fix read out of buffer bounds under - soup_multipart_new_from_message() - -This is CVE-2025-32914, special crafted input can cause read out of buffer bounds -of the body argument. - -Closes #436 ---- - libsoup/soup-multipart.c | 2 +- - tests/multipart-test.c | 58 ++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 59 insertions(+), 1 deletion(-) - -diff --git a/libsoup/soup-multipart.c b/libsoup/soup-multipart.c -index 2421c91f8..102ce3722 100644 ---- a/libsoup/soup-multipart.c -+++ b/libsoup/soup-multipart.c -@@ -173,7 +173,7 @@ soup_multipart_new_from_message (SoupMessageHeaders *headers, - return NULL; - } - -- split = strstr (start, "\r\n\r\n"); -+ split = g_strstr_len (start, body_end - start, "\r\n\r\n"); - if (!split || split > end) { - soup_multipart_free (multipart); - return NULL; -diff --git a/tests/multipart-test.c b/tests/multipart-test.c -index 2c0e7e969..f5b986889 100644 ---- a/tests/multipart-test.c -+++ b/tests/multipart-test.c -@@ -471,6 +471,62 @@ test_multipart (gconstpointer data) - loop = NULL; - } - -+static void -+test_multipart_bounds_good (void) -+{ -+ #define TEXT "line1\r\nline2" -+ SoupMultipart *multipart; -+ SoupMessageHeaders *headers, *set_headers = NULL; -+ GBytes *bytes, *set_bytes = NULL; -+ const char *raw_data = "--123\r\nContent-Type: text/plain;\r\n\r\n" TEXT "\r\n--123--\r\n"; -+ gboolean success; -+ -+ headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART); -+ soup_message_headers_append (headers, "Content-Type", "multipart/mixed; boundary=\"123\""); -+ -+ bytes = g_bytes_new (raw_data, strlen (raw_data)); -+ -+ multipart = soup_multipart_new_from_message (headers, bytes); -+ -+ g_assert_nonnull (multipart); -+ g_assert_cmpint (soup_multipart_get_length (multipart), ==, 1); -+ success = soup_multipart_get_part (multipart, 0, &set_headers, &set_bytes); -+ g_assert_true (success); -+ g_assert_nonnull (set_headers); -+ g_assert_nonnull (set_bytes); -+ g_assert_cmpint (strlen (TEXT), ==, g_bytes_get_size (set_bytes)); -+ g_assert_cmpstr ("text/plain", ==, soup_message_headers_get_content_type (set_headers, NULL)); -+ g_assert_cmpmem (TEXT, strlen (TEXT), g_bytes_get_data (set_bytes, NULL), g_bytes_get_size (set_bytes)); -+ -+ soup_message_headers_unref (headers); -+ g_bytes_unref (bytes); -+ -+ soup_multipart_free (multipart); -+ -+ #undef TEXT -+} -+ -+static void -+test_multipart_bounds_bad (void) -+{ -+ SoupMultipart *multipart; -+ SoupMessageHeaders *headers; -+ GBytes *bytes; -+ const char *raw_data = "--123\r\nContent-Type: text/plain;\r\nline1\r\nline2\r\n--123--\r\n"; -+ -+ headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART); -+ soup_message_headers_append (headers, "Content-Type", "multipart/mixed; boundary=\"123\""); -+ -+ bytes = g_bytes_new (raw_data, strlen (raw_data)); -+ -+ /* it did read out of raw_data/bytes bounds */ -+ multipart = soup_multipart_new_from_message (headers, bytes); -+ g_assert_null (multipart); -+ -+ soup_message_headers_unref (headers); -+ g_bytes_unref (bytes); -+} -+ - int - main (int argc, char **argv) - { -@@ -498,6 +554,8 @@ 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-good", test_multipart_bounds_good); -+ g_test_add_func ("/multipart/bounds-bad", test_multipart_bounds_bad); - - ret = g_test_run (); - --- -GitLab - diff --git a/CVE-2025-4035.patch b/CVE-2025-4035.patch index 06e0291..fe63ab9 100644 --- a/CVE-2025-4035.patch +++ b/CVE-2025-4035.patch @@ -1,7 +1,7 @@ -From 845e198402377f8644e9ae5200f1715df1ddfc08 Mon Sep 17 00:00:00 2001 +From 3b0a3a4a9c751e2874d8cfb7b61349881ba2114a Mon Sep 17 00:00:00 2001 From: Patrick Griffis Date: Thu, 27 Mar 2025 14:43:26 -0500 -Subject: [PATCH 1/3] cookie: Always normalize domain value +Subject: [PATCH] cookie: Always normalize domain value In order for libpsl to give accurate results the domain must be lowercased. @@ -16,7 +16,7 @@ To make it easiest we normalize it at construction time of the cookie. 6 files changed, 76 insertions(+), 7 deletions(-) diff --git a/libsoup/cookies/soup-cookie-jar.c b/libsoup/cookies/soup-cookie-jar.c -index fac53a5f9..7f92ace1f 100644 +index 20805c20..51c51d3c 100644 --- a/libsoup/cookies/soup-cookie-jar.c +++ b/libsoup/cookies/soup-cookie-jar.c @@ -519,6 +519,7 @@ incoming_cookie_is_third_party (SoupCookieJar *jar, @@ -48,7 +48,7 @@ index fac53a5f9..7f92ace1f 100644 if (policy == SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY) return TRUE; diff --git a/libsoup/cookies/soup-cookie.c b/libsoup/cookies/soup-cookie.c -index cc80d001f..b6d771646 100644 +index d4a9f4c0..e0dbbdda 100644 --- a/libsoup/cookies/soup-cookie.c +++ b/libsoup/cookies/soup-cookie.c @@ -168,6 +168,16 @@ parse_date (const char **val_p) @@ -109,7 +109,7 @@ index cc80d001f..b6d771646 100644 /** diff --git a/libsoup/soup-tld.c b/libsoup/soup-tld.c -index 2d8151662..71fac5749 100644 +index 02e54059..ee98c459 100644 --- a/libsoup/soup-tld.c +++ b/libsoup/soup-tld.c @@ -15,6 +15,7 @@ @@ -152,23 +152,21 @@ index 2d8151662..71fac5749 100644 /** diff --git a/libsoup/soup-uri-utils-private.h b/libsoup/soup-uri-utils-private.h -index 0119f0814..c2f984f86 100644 +index a73e8821..83c4fb36 100644 --- a/libsoup/soup-uri-utils-private.h +++ b/libsoup/soup-uri-utils-private.h -@@ -28,6 +28,8 @@ GUri *soup_uri_copy_with_normalized_flags (GUri *uri); +@@ -30,4 +30,6 @@ GUri *soup_uri_copy_with_normalized_flags (GUri *uri); char *soup_uri_get_host_for_headers (GUri *uri); +char *soup_uri_normalize_domain (const char *domain); + - #define SOUP_URI_IS_VALID(x) ((x) && g_uri_get_host(x) && g_uri_get_host(x)[0]) - G_END_DECLS diff --git a/libsoup/soup-uri-utils.c b/libsoup/soup-uri-utils.c -index 0963a1143..1f65faede 100644 +index 44cc59b1..63262018 100644 --- a/libsoup/soup-uri-utils.c +++ b/libsoup/soup-uri-utils.c -@@ -506,3 +506,21 @@ soup_uri_get_host_for_headers (GUri *uri) +@@ -566,3 +566,21 @@ soup_uri_get_host_for_headers (GUri *uri) return g_strdup (host); } @@ -191,10 +189,10 @@ index 0963a1143..1f65faede 100644 + return normalized; +} diff --git a/tests/cookies-test.c b/tests/cookies-test.c -index 1d2d45630..7007aaf59 100644 +index 27cf6d9b..f3dfd6df 100644 --- a/tests/cookies-test.c +++ b/tests/cookies-test.c -@@ -695,6 +695,24 @@ do_cookies_threads_test (void) +@@ -715,6 +715,24 @@ do_cookies_threads_test (void) soup_test_session_abort_unref (session); } @@ -219,7 +217,7 @@ index 1d2d45630..7007aaf59 100644 int main (int argc, char **argv) { -@@ -726,6 +744,7 @@ main (int argc, char **argv) +@@ -748,6 +766,7 @@ main (int argc, char **argv) g_test_add_func ("/cookies/secure-cookies", do_cookies_strict_secure_test); g_test_add_func ("/cookies/prefix", do_cookies_prefix_test); g_test_add_func ("/cookies/threads", do_cookies_threads_test); @@ -228,4 +226,5 @@ index 1d2d45630..7007aaf59 100644 ret = g_test_run (); -- -GitLab +2.54.0 + diff --git a/CVE-2025-4945-CVE-2025-11021.patch b/CVE-2025-4945-CVE-2025-11021.patch deleted file mode 100644 index 29b5680..0000000 --- a/CVE-2025-4945-CVE-2025-11021.patch +++ /dev/null @@ -1,245 +0,0 @@ -From 8988379984e33dcc7d3aa58551db13e48755959f Mon Sep 17 00:00:00 2001 -From: Milan Crha -Date: Thu, 15 May 2025 07:59:14 +0200 -Subject: [PATCH 1/2] soup-date-utils: Add value checks for date/time parsing - -Reject date/time when it does not represent a valid value. - -Closes https://gitlab.gnome.org/GNOME/libsoup/-/issues/448 ---- - libsoup/soup-date-utils.c | 23 +++++++++++++++-------- - tests/cookies-test.c | 10 ++++++++++ - 2 files changed, 25 insertions(+), 8 deletions(-) - -diff --git a/libsoup/soup-date-utils.c b/libsoup/soup-date-utils.c -index fd785f509..34ca99503 100644 ---- a/libsoup/soup-date-utils.c -+++ b/libsoup/soup-date-utils.c -@@ -129,7 +129,7 @@ parse_day (int *day, const char **date_string) - while (*end == ' ' || *end == '-') - end++; - *date_string = end; -- return TRUE; -+ return *day >= 1 && *day <= 31; - } - - static inline gboolean -@@ -169,7 +169,7 @@ parse_year (int *year, const char **date_string) - while (*end == ' ' || *end == '-') - end++; - *date_string = end; -- return TRUE; -+ return *year > 0 && *year < 9999; - } - - static inline gboolean -@@ -193,7 +193,7 @@ parse_time (int *hour, int *minute, int *second, const char **date_string) - while (*p == ' ') - p++; - *date_string = p; -- return TRUE; -+ return *hour >= 0 && *hour < 24 && *minute >= 0 && *minute < 60 && *second >= 0 && *second < 60; - } - - static inline gboolean -@@ -209,9 +209,14 @@ parse_timezone (GTimeZone **timezone, const char **date_string) - gulong val; - int sign = (**date_string == '+') ? 1 : -1; - val = strtoul (*date_string + 1, (char **)date_string, 10); -- if (**date_string == ':') -- val = 60 * val + strtoul (*date_string + 1, (char **)date_string, 10); -- else -+ if (val > 9999) -+ return FALSE; -+ if (**date_string == ':') { -+ gulong val2 = strtoul (*date_string + 1, (char **)date_string, 10); -+ if (val > 99 || val2 > 99) -+ return FALSE; -+ val = 60 * val + val2; -+ } else - val = 60 * (val / 100) + (val % 100); - offset_minutes = sign * val; - utc = (sign == -1) && !val; -@@ -264,7 +269,8 @@ parse_textual_date (const char *date_string) - if (!parse_month (&month, &date_string) || - !parse_day (&day, &date_string) || - !parse_time (&hour, &minute, &second, &date_string) || -- !parse_year (&year, &date_string)) -+ !parse_year (&year, &date_string) || -+ !g_date_valid_dmy (day, month, year)) - return NULL; - - /* There shouldn't be a timezone, but check anyway */ -@@ -276,7 +282,8 @@ parse_textual_date (const char *date_string) - if (!parse_day (&day, &date_string) || - !parse_month (&month, &date_string) || - !parse_year (&year, &date_string) || -- !parse_time (&hour, &minute, &second, &date_string)) -+ !parse_time (&hour, &minute, &second, &date_string) || -+ !g_date_valid_dmy (day, month, year)) - return NULL; - - /* This time there *should* be a timezone, but we -diff --git a/tests/cookies-test.c b/tests/cookies-test.c -index 1d2d45630..ff809a400 100644 ---- a/tests/cookies-test.c -+++ b/tests/cookies-test.c -@@ -460,6 +460,15 @@ do_cookies_parsing_max_age_long_overflow (void) - soup_cookie_free (cookie); - } - -+static void -+do_cookies_parsing_int32_overflow (void) -+{ -+ SoupCookie *cookie = soup_cookie_parse ("Age=1;expires=3Mar9 999:9:9+ 999999999-age=main=gne=", NULL); -+ g_assert_nonnull (cookie); -+ g_assert_null (soup_cookie_get_expires (cookie)); -+ soup_cookie_free (cookie); -+} -+ - static void - do_cookies_equal_nullpath (void) - { -@@ -718,6 +727,7 @@ main (int argc, char **argv) - g_test_add_func ("/cookies/parsing/no-path-null-origin", do_cookies_parsing_nopath_nullorigin); - g_test_add_func ("/cookies/parsing/max-age-int32-overflow", do_cookies_parsing_max_age_int32_overflow); - g_test_add_func ("/cookies/parsing/max-age-long-overflow", do_cookies_parsing_max_age_long_overflow); -+ g_test_add_func ("/cookies/parsing/int32-overflow", do_cookies_parsing_int32_overflow); - g_test_add_func ("/cookies/parsing/equal-nullpath", do_cookies_equal_nullpath); - g_test_add_func ("/cookies/parsing/control-characters", do_cookies_parsing_control_characters); - g_test_add_func ("/cookies/parsing/name-value-max-size", do_cookies_parsing_name_value_max_size); --- -GitLab - - -From 0d6bfa52da7313de848fb13fcfdbc561c04afef8 Mon Sep 17 00:00:00 2001 -From: Brian Yurko <155515-byurko@users.noreply.gitlab.gnome.org> -Date: Wed, 11 Jun 2025 11:00:56 -0400 -Subject: [PATCH 2/2] tests: Add tests for date-time including timezone - validation work - -These tests are built on top of earlier work in a related pull request. - -Closes #448 ---- - libsoup/soup-date-utils.c | 8 ++++---- - tests/cookies-test.c | 1 + - tests/date-test.c | 37 ++++++++++++++++++++++++++++++------- - 3 files changed, 35 insertions(+), 11 deletions(-) - -diff --git a/libsoup/soup-date-utils.c b/libsoup/soup-date-utils.c -index 34ca99503..73f80ab60 100644 ---- a/libsoup/soup-date-utils.c -+++ b/libsoup/soup-date-utils.c -@@ -40,7 +40,7 @@ soup_date_time_is_past (GDateTime *date) - g_return_val_if_fail (date != NULL, TRUE); - - /* optimization */ -- if (g_date_time_get_year (date) < 2020) -+ if (g_date_time_get_year (date) < 2025) - return TRUE; - - return g_date_time_to_unix (date) < time (NULL); -@@ -219,15 +219,15 @@ parse_timezone (GTimeZone **timezone, const char **date_string) - } else - val = 60 * (val / 100) + (val % 100); - offset_minutes = sign * val; -- utc = (sign == -1) && !val; -+ utc = (sign == -1) && !val; - } else if (**date_string == 'Z') { - offset_minutes = 0; -- utc = TRUE; -+ utc = TRUE; - (*date_string)++; - } else if (!strcmp (*date_string, "GMT") || - !strcmp (*date_string, "UTC")) { - offset_minutes = 0; -- utc = TRUE; -+ utc = TRUE; - (*date_string) += 3; - } else if (strchr ("ECMP", **date_string) && - ((*date_string)[1] == 'D' || (*date_string)[1] == 'S') && -diff --git a/tests/cookies-test.c b/tests/cookies-test.c -index ff809a400..18c9b60d8 100644 ---- a/tests/cookies-test.c -+++ b/tests/cookies-test.c -@@ -464,6 +464,7 @@ static void - do_cookies_parsing_int32_overflow (void) - { - SoupCookie *cookie = soup_cookie_parse ("Age=1;expires=3Mar9 999:9:9+ 999999999-age=main=gne=", NULL); -+ g_test_bug ("https://gitlab.gnome.org/GNOME/libsoup/-/issues/448"); - g_assert_nonnull (cookie); - g_assert_null (soup_cookie_get_expires (cookie)); - soup_cookie_free (cookie); -diff --git a/tests/date-test.c b/tests/date-test.c -index 7eefd7c00..abf89ddd1 100644 ---- a/tests/date-test.c -+++ b/tests/date-test.c -@@ -92,6 +92,11 @@ static const OkDate ok_dates[] = { - { "Sat, 06 Nov 2004 08:09:07", NULL }, - { "06 Nov 2004 08:09:07 GMT", NULL }, - { "SAT, 06 NOV 2004 08:09:07 +1000", "644048" }, -+ { "Sat, 06-Nov-2004 08:09:07 -10000", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/448" }, -+ { "Sat, 06-Nov-2004 08:09:07 +01:30", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/448" }, -+ { "Sat, 06-Nov-2004 08:09:07 +0:180", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/448" }, -+ { "Sat, 06-Nov-2004 08:09:07 +100:100", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/448" }, -+ { "Sat, 06-Nov-2004 08:09:07 Z", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/448" }, - - /* rfc850-date, and broken variants */ - { "Saturday, 06-Nov-04 08:09:07 GMT", NULL }, -@@ -109,6 +114,8 @@ static const OkDate ok_dates[] = { - { "Sat Nov 06 08:09:07 2004", NULL }, - { "Sat Nov 6 08:09:07 2004", NULL }, - { "Sat Nov 6 08:09:07 2004 GMT", NULL }, -+ { "Sat Nov 6 08:09:07 2004 NoZone", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/448" }, -+ { "Sat Nov 6 08:09:07 2004 UTC", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/448" }, - - /* Netscape cookie spec date, and broken variants */ - { "Sat, 06-Nov-2004 08:09:07 GMT", NULL }, -@@ -182,7 +189,23 @@ static const BadDate bad_dates[] = { - { "Sat Nov 6 08::07 2004", NULL }, - { "Sat Nov 6 08:09: 2004", NULL }, - { "Sat Nov 6 08:09:07", NULL }, -- { "Sat Nov 6 08:09:07 GMT 2004", NULL } -+ { "Sat Nov 6 08:09:07 GMT 2004", NULL }, -+ -+ /* range constraints added "https://gitlab.gnome.org/GNOME/libsoup/-/issues/448" */ -+ { "Sat, 00-Nov-2004 08:09:07 GMT", NULL }, -+ { "Sat, 32-Nov-2004 08:09:07 GMT", NULL }, -+ { "Sat, 06-Nov-0 08:09:07 GMT", NULL }, -+ { "Sat, 06-Nov-9999 08:09:07 GMT", NULL }, -+ { "Sat, 06-Nov-2004 0-1:09:07 GMT", NULL }, -+ { "(Sat), Nov 6 -1:09:07 2004", NULL }, -+ { "Sat, 06-Nov-2004 24:09:07 GMT", NULL }, -+ { "Sat, 06-Nov-2004 08:-1:07 GMT", NULL }, -+ { "Sat, 06-Nov-2004 08:60:07 GMT", NULL }, -+ { "Sat, 06-Nov-2004 08:09:-10 GMT", NULL }, -+ { "Sat, 06-Nov-2004 08:09:60 GMT", NULL }, -+ { "Sat, 06-Nov-71 08:09:99 UTC", NULL }, -+ { "Sat, 31-Feb-2004 08:09:07 UTC", NULL }, -+ { "2004-11-06T08:09:07Z", NULL } - }; - - static void -@@ -198,12 +221,12 @@ check_bad (gconstpointer data) - soup_test_assert (date == NULL, - "date parsing succeeded for '%s': %d %d %d - %d %d %d", - bad->date, -- g_date_time_get_year (date), -- g_date_time_get_month (date), -- g_date_time_get_day_of_month (date), -- g_date_time_get_hour (date), -- g_date_time_get_minute (date), -- g_date_time_get_second (date)); -+ g_date_time_get_year (date), -+ g_date_time_get_month (date), -+ g_date_time_get_day_of_month (date), -+ g_date_time_get_hour (date), -+ g_date_time_get_minute (date), -+ g_date_time_get_second (date)); - g_clear_pointer (&date, g_date_time_unref); - } - --- -GitLab - diff --git a/CVE-2025-4948.patch b/CVE-2025-4948.patch deleted file mode 100644 index cb71c6a..0000000 --- a/CVE-2025-4948.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 66521f00e9f87f709d8ad9138f19052db933cf06 Mon Sep 17 00:00:00 2001 -From: Milan Crha -Date: Thu, 15 May 2025 17:49:11 +0200 -Subject: [PATCH] soup-multipart: Verify boundary limits for multipart body - -It could happen that the boundary started at a place which resulted into -a negative number, which in an unsigned integer is a very large value. -Check the body size is not a negative value before setting it. - -Closes https://gitlab.gnome.org/GNOME/libsoup/-/issues/449 ---- - libsoup/soup-multipart.c | 2 +- - tests/multipart-test.c | 40 ++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 41 insertions(+), 1 deletion(-) - -diff --git a/libsoup/soup-multipart.c b/libsoup/soup-multipart.c -index 102ce372..a587fe7c 100644 ---- a/libsoup/soup-multipart.c -+++ b/libsoup/soup-multipart.c -@@ -204,7 +204,7 @@ soup_multipart_new_from_message (SoupMessageHeaders *headers, - */ - part_body = g_bytes_new_from_bytes (body, // FIXME - split - body_data, -- end - 2 - split); -+ end - 2 >= split ? end - 2 - split : 0); - g_ptr_array_add (multipart->bodies, part_body); - - start = end; -diff --git a/tests/multipart-test.c b/tests/multipart-test.c -index f5b98688..92b673eb 100644 ---- a/tests/multipart-test.c -+++ b/tests/multipart-test.c -@@ -527,6 +527,45 @@ test_multipart_bounds_bad (void) - g_bytes_unref (bytes); - } - -+static void -+test_multipart_too_large (void) -+{ -+ const char *raw_body = -+ "-------------------\r\n" -+ "-\n" -+ "Cont\"\r\n" -+ "Content-Tynt----e:n\x8erQK\r\n" -+ "Content-Disposition: name= form-; name=\"file\"; filename=\"ype:i/ -d; ----\xae\r\n" -+ "Content-Typimag\x01/png--\\\n" -+ "\r\n" -+ "---:\n\r\n" -+ "\r\n" -+ "-------------------------------------\r\n" -+ "---------\r\n" -+ "----------------------"; -+ GBytes *body; -+ GHashTable *params; -+ SoupMessageHeaders *headers; -+ SoupMultipart *multipart; -+ -+ params = g_hash_table_new (g_str_hash, g_str_equal); -+ g_hash_table_insert (params, (gpointer) "boundary", (gpointer) "-----------------"); -+ headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART); -+ soup_message_headers_set_content_type (headers, "multipart/form-data", params); -+ g_hash_table_unref (params); -+ -+ body = g_bytes_new_static (raw_body, strlen (raw_body)); -+ multipart = soup_multipart_new_from_message (headers, body); -+ soup_message_headers_unref (headers); -+ g_bytes_unref (body); -+ -+ g_assert_nonnull (multipart); -+ g_assert_cmpint (soup_multipart_get_length (multipart), ==, 1); -+ g_assert_true (soup_multipart_get_part (multipart, 0, &headers, &body)); -+ g_assert_cmpint (g_bytes_get_size (body), ==, 0); -+ soup_multipart_free (multipart); -+} -+ - int - main (int argc, char **argv) - { -@@ -556,6 +595,7 @@ main (int argc, char **argv) - g_test_add_data_func ("/multipart/async-small-reads", GINT_TO_POINTER (ASYNC_MULTIPART_SMALL_READS), test_multipart); - g_test_add_func ("/multipart/bounds-good", test_multipart_bounds_good); - g_test_add_func ("/multipart/bounds-bad", test_multipart_bounds_bad); -+ g_test_add_func ("/multipart/too-large", test_multipart_too_large); - - ret = g_test_run (); - --- -GitLab - diff --git a/CVE-2026-0719.patch b/CVE-2026-0719.patch deleted file mode 100644 index f528d07..0000000 --- a/CVE-2026-0719.patch +++ /dev/null @@ -1,185 +0,0 @@ -From 427a5ed7048dda4d22f13c164a3a439e68604406 Mon Sep 17 00:00:00 2001 -From: Mike Gorse -Date: Thu, 8 Jan 2026 16:19:37 -0600 -Subject: [PATCH] soup-auth-ntlm: Reject excessively long passwords - -According to -https://learn.microsoft.com/en-us/troubleshoot/windows-server/windows-security/ntlm-user-authentication, -the practical limit for a NTLM password is 128 Unicode characters, so it -should be safe to reject passwords longer than 256 bytes. Previously, -md4sum could overflow and cause an out-of-bounds memory access if an -extremely long password was provided. Also update md4sum to use unsigned -variables for size-related calculations, as a precaution. - -This is CVE-2026-0719. - -Closes #477. ---- - libsoup/auth/soup-auth-ntlm.c | 27 +++++++++++---- - tests/ntlm-test.c | 64 +++++++++++++++++++++++++++++++++++ - 2 files changed, 84 insertions(+), 7 deletions(-) - -diff --git a/libsoup/auth/soup-auth-ntlm.c b/libsoup/auth/soup-auth-ntlm.c -index dc440ad1..a338389b 100644 ---- a/libsoup/auth/soup-auth-ntlm.c -+++ b/libsoup/auth/soup-auth-ntlm.c -@@ -355,6 +355,14 @@ soup_auth_ntlm_update_connection (SoupConnectionAuth *auth, SoupMessage *msg, - return FALSE; - } - -+ if (priv->password_state == SOUP_NTLM_PASSWORD_PROVIDED && !priv->nt_hash[0]) { -+ /* This can happen if an excessively long password was -+ * provided, in which case we don't try to hash */ -+ conn->state = SOUP_NTLM_FAILED; -+ priv->password_state = SOUP_NTLM_PASSWORD_REJECTED; -+ return TRUE; -+ } -+ - if (!soup_ntlm_parse_challenge (auth_header + 5, &conn->nonce, - priv->domain ? NULL : &priv->domain, - &conn->ntlmv2_session, &conn->negotiate_target, -@@ -449,8 +457,10 @@ soup_auth_ntlm_authenticate (SoupAuth *auth, const char *username, - priv->username = g_strdup (username); - } - -- soup_ntlm_nt_hash (password, priv->nt_hash); -- soup_ntlm_lanmanager_hash (password, priv->lm_hash); -+ if (strlen (password) < 256) { -+ soup_ntlm_nt_hash (password, priv->nt_hash); -+ soup_ntlm_lanmanager_hash (password, priv->lm_hash); -+ } - - priv->password_state = SOUP_NTLM_PASSWORD_PROVIDED; - } -@@ -616,7 +626,7 @@ soup_auth_ntlm_class_init (SoupAuthNTLMClass *auth_ntlm_class) - } - - static void md4sum (const unsigned char *in, -- int nbytes, -+ size_t nbytes, - unsigned char digest[16]); - - typedef guint32 DES_KS[16][2]; /* Single-key DES key schedule */ -@@ -662,7 +672,7 @@ soup_ntlm_nt_hash (const char *password, guchar hash[21]) - { - unsigned char *buf, *p; - -- p = buf = g_malloc (strlen (password) * 2); -+ p = buf = g_malloc_n (strlen (password), 2); - - while (*password) { - *p++ = *password++; -@@ -1104,15 +1114,16 @@ calc_response (const guchar *key, const guchar *plaintext, guchar *results) - #define ROT(val, n) ( ((val) << (n)) | ((val) >> (32 - (n))) ) - - static void --md4sum (const unsigned char *in, int nbytes, unsigned char digest[16]) -+md4sum (const unsigned char *in, size_t nbytes, unsigned char digest[16]) - { - unsigned char *M; - guint32 A, B, C, D, AA, BB, CC, DD, X[16]; -- int pbytes, nbits = nbytes * 8, i, j; -+ size_t pbytes, nbits = nbytes * 8; -+ int i, j; - - /* There is *always* padding of at least one bit. */ - pbytes = ((119 - (nbytes % 64)) % 64) + 1; -- M = alloca (nbytes + pbytes + 8); -+ M = g_malloc (nbytes + pbytes + 8); - memcpy (M, in, nbytes); - memset (M + nbytes, 0, pbytes + 8); - M[nbytes] = 0x80; -@@ -1212,6 +1223,8 @@ md4sum (const unsigned char *in, int nbytes, unsigned char digest[16]) - digest[13] = (D >> 8) & 0xFF; - digest[14] = (D >> 16) & 0xFF; - digest[15] = (D >> 24) & 0xFF; -+ -+ g_free (M); - } - - -diff --git a/tests/ntlm-test.c b/tests/ntlm-test.c -index e19f5663..c95fcd50 100644 ---- a/tests/ntlm-test.c -+++ b/tests/ntlm-test.c -@@ -740,6 +740,67 @@ do_retrying_test (TestServer *ts, - soup_test_session_abort_unref (session); - } - -+static gboolean -+long_password_test_authenticate (SoupMessage *msg, -+ SoupAuth *auth, -+ gboolean retrying, -+ gpointer user) -+{ -+ size_t l = 65536; -+ char *password; -+ char tmp[10000]; -+ size_t i; -+ -+ password = (char *)g_malloc (l); -+ -+ for (i = 0; i < 10000; i++) { -+ tmp[i] = 'A'; -+ } -+ for (i = 0; i < l/10000; i++) { -+ memcpy (password + i * 10000, tmp, 10000); -+ } -+ memcpy (password + l - 1 - 10000, tmp, 10000); -+ -+ soup_auth_authenticate (auth, "alice", password); -+ -+ g_free (password); -+ return TRUE; -+} -+ -+static void -+do_long_password_test (TestServer *ts, -+ gconstpointer data) -+{ -+ SoupSession *session; -+ SoupMessage *msg; -+ GUri *uri; -+ GBytes *body; -+ -+ if (!can_do_ntlm_test ()) { -+ g_test_skip ("NTLM authentication not available (likely due to FIPS mode)"); -+ return; -+ } -+ -+ session = soup_test_session_new (NULL); -+ soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM); -+ soup_session_set_proxy_resolver(session, NULL); -+ -+ uri = g_uri_parse_relative (ts->uri, "/alice", SOUP_HTTP_URI_FLAGS, NULL); -+ msg = soup_message_new_from_uri ("GET", uri); -+ g_signal_connect (msg, "authenticate", -+ G_CALLBACK (long_password_test_authenticate), NULL); -+ g_uri_unref (uri); -+ -+ body = soup_session_send_and_read (session, msg, NULL, NULL); -+ -+ soup_test_assert_message_status (msg, SOUP_STATUS_UNAUTHORIZED); -+ -+ g_bytes_unref (body); -+ g_object_unref (msg); -+ -+ soup_test_session_abort_unref (session); -+} -+ - int - main (int argc, char **argv) - { -@@ -763,6 +824,9 @@ main (int argc, char **argv) - g_test_add ("/ntlm/retry", TestServer, NULL, - setup_server, do_retrying_test, teardown_server); - -+ g_test_add ("/ntlm/long-password", TestServer, NULL, -+ setup_server, do_long_password_test, teardown_server); -+ - ret = g_test_run (); - - test_cleanup (); --- -2.52.0 - diff --git a/CVE-2026-1761.patch b/CVE-2026-1761.patch deleted file mode 100644 index 729b454..0000000 --- a/CVE-2026-1761.patch +++ /dev/null @@ -1,97 +0,0 @@ -From 0a55f5ebde48cc3e488bff70215b22f103cc6077 Mon Sep 17 00:00:00 2001 -From: Carlos Garcia Campos -Date: Mon, 19 Jan 2026 15:14:58 +0100 -Subject: [PATCH] multipart: check length of bytes read - soup_filter_input_stream_read_until() - -We do make sure the read length is smaller than the buffer length when -the boundary is not found, but we should do the same when the boundary -is found. - -Spotted in #YWH-PGM9867-149 -Closes #493 ---- - libsoup/soup-filter-input-stream.c | 3 +- - tests/multipart-test.c | 46 ++++++++++++++++++++++++++++++ - 2 files changed, 48 insertions(+), 1 deletion(-) - -diff --git a/libsoup/soup-filter-input-stream.c b/libsoup/soup-filter-input-stream.c -index b1e616c7..22541aa0 100644 ---- a/libsoup/soup-filter-input-stream.c -+++ b/libsoup/soup-filter-input-stream.c -@@ -337,6 +337,7 @@ soup_filter_input_stream_read_until (SoupFilterInputStream *fstream, - if (eof && !*got_boundary) - read_length = MIN (priv->buf->len, length); - else -- read_length = p - buf; -+ read_length = MIN ((gsize)(p - buf), length); -+ - return read_from_buf (fstream, buffer, read_length); - } -diff --git a/tests/multipart-test.c b/tests/multipart-test.c -index 92b673eb..0496a4c6 100644 ---- a/tests/multipart-test.c -+++ b/tests/multipart-test.c -@@ -527,6 +527,51 @@ test_multipart_bounds_bad (void) - g_bytes_unref (bytes); - } - -+static void -+test_multipart_bounds_bad_3 (void) -+{ -+ SoupMessage *msg; -+ SoupMessageHeaders *headers; -+ GInputStream *in; -+ SoupMultipartInputStream *multipart; -+ GError *error = NULL; -+ const char raw_data[] = "\0$--A\r\nContent-Disposition: form-data; name=\"f\"\r\n\r\nXXXXXXXXX\r\n--A--\r\n"; -+ -+ msg = soup_message_new(SOUP_METHOD_POST, "http://foo/upload"); -+ headers = soup_message_get_response_headers (msg); -+ soup_message_headers_replace (headers, "Content-Type", "multipart/form-data; boundary=\"A\""); -+ -+ in = g_memory_input_stream_new_from_data (raw_data + 2, sizeof(raw_data) - 2, NULL); -+ multipart = soup_multipart_input_stream_new (msg, in); -+ g_object_unref (in); -+ -+ while (TRUE) { -+ in = soup_multipart_input_stream_next_part (multipart, NULL, &error); -+ g_assert_no_error (error); -+ if (!in) { -+ g_clear_error (&error); -+ break; -+ } -+ -+ char buffer[10]; -+ while (TRUE) { -+ gssize bytes_read; -+ -+ bytes_read = g_input_stream_read (in, buffer, sizeof(buffer), NULL, &error); -+ g_assert_no_error (error); -+ if (bytes_read <= 0) { -+ g_clear_error (&error); -+ break; -+ } -+ } -+ -+ g_object_unref (in); -+ } -+ -+ g_object_unref (multipart); -+ g_object_unref (msg); -+} -+ - static void - test_multipart_too_large (void) - { -@@ -595,6 +640,7 @@ main (int argc, char **argv) - g_test_add_data_func ("/multipart/async-small-reads", GINT_TO_POINTER (ASYNC_MULTIPART_SMALL_READS), test_multipart); - g_test_add_func ("/multipart/bounds-good", test_multipart_bounds_good); - g_test_add_func ("/multipart/bounds-bad", test_multipart_bounds_bad); -+ g_test_add_func ("/multipart/bounds-bad-3", test_multipart_bounds_bad_3); - g_test_add_func ("/multipart/too-large", test_multipart_too_large); - - ret = g_test_run (); --- -2.52.0 - diff --git a/CVE-2026-4271.patch b/CVE-2026-4271.patch new file mode 100644 index 0000000..567e6a9 --- /dev/null +++ b/CVE-2026-4271.patch @@ -0,0 +1,361 @@ +From f99a814cadc4c19c5b70a42476f05e62ac2d5bab Mon Sep 17 00:00:00 2001 +From: Carlos Garcia Campos +Date: Mon, 16 Feb 2026 12:09:08 +0100 +Subject: [PATCH] server: protect message io while reading and writing + +Ensure the nghttp2 session is not destroyed while being used. + +Closes #496 +--- + .../http2/soup-server-message-io-http2.c | 117 +++++++++++++----- + tests/http2-test.c | 54 ++++++++ + 2 files changed, 141 insertions(+), 30 deletions(-) + +diff --git a/libsoup/server/http2/soup-server-message-io-http2.c b/libsoup/server/http2/soup-server-message-io-http2.c +index 913afb46..6f8d1bb6 100644 +--- a/libsoup/server/http2/soup-server-message-io-http2.c ++++ b/libsoup/server/http2/soup-server-message-io-http2.c +@@ -69,6 +69,8 @@ typedef struct { + GHashTable *messages; + + guint in_callback; ++ guint protected; ++ gboolean destroyed; + } SoupServerMessageIOHTTP2; + + static void soup_server_message_io_http2_send_response (SoupServerMessageIOHTTP2 *io, +@@ -146,6 +148,8 @@ soup_server_message_io_http2_destroy (SoupServerMessageIO *iface) + { + SoupServerMessageIOHTTP2 *io = (SoupServerMessageIOHTTP2 *)iface; + ++ io->destroyed = TRUE; ++ + if (io->read_source) { + g_source_destroy (io->read_source); + g_source_unref (io->read_source); +@@ -160,10 +164,14 @@ soup_server_message_io_http2_destroy (SoupServerMessageIO *iface) + } + + g_clear_object (&io->iostream); +- g_clear_pointer (&io->session, nghttp2_session_del); +- g_clear_pointer (&io->messages, g_hash_table_unref); ++ io->istream = NULL; ++ io->ostream = NULL; + +- g_free (io); ++ if (io->protected == 0) { ++ g_clear_pointer (&io->session, nghttp2_session_del); ++ g_clear_pointer (&io->messages, g_hash_table_unref); ++ g_free (io); ++ } + } + + static void +@@ -321,7 +329,33 @@ static const SoupServerMessageIOFuncs io_funcs = { + soup_server_message_io_http2_is_paused + }; + ++static void ++soup_server_message_io_http2_protect (SoupServerMessageIOHTTP2 *io) ++{ ++ io->protected++; ++ g_object_ref (io->conn); ++} ++ + static gboolean ++soup_server_message_io_http2_unprotect (SoupServerMessageIOHTTP2 *io) ++{ ++ g_object_unref (io->conn); ++ ++ if (--io->protected > 0) ++ return FALSE; ++ ++ if (io->destroyed) { ++ g_clear_pointer (&io->session, nghttp2_session_del); ++ g_clear_pointer (&io->messages, g_hash_table_unref); ++ g_free (io); ++ ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static void + io_write (SoupServerMessageIOHTTP2 *io, + GError **error) + { +@@ -336,51 +370,57 @@ io_write (SoupServerMessageIOHTTP2 *io, + if (io->write_buffer_size == 0) { + /* Done */ + io->write_buffer = NULL; +- return TRUE; ++ return; + } + } + ++ if (!io->ostream) ++ return; ++ + gssize ret = g_pollable_stream_write (io->ostream, + io->write_buffer + io->written_bytes, + io->write_buffer_size - io->written_bytes, + FALSE, NULL, error); +- if (ret < 0) +- return FALSE; +- +- io->written_bytes += ret; +- return TRUE; ++ if (ret > 0) ++ io->written_bytes += ret; + } + + static gboolean + io_write_ready (GObject *stream, + SoupServerMessageIOHTTP2 *io) + { +- SoupServerConnection *conn = io->conn; + GError *error = NULL; + +- g_object_ref (conn); ++ soup_server_message_io_http2_protect (io); ++ ++ while (!error) { ++ if (io->destroyed) ++ break; ++ ++ if (!nghttp2_session_want_write (io->session)) ++ break; + +- while (!error && soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io && nghttp2_session_want_write (io->session)) + io_write (io, &error); ++ } + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + g_error_free (error); +- g_object_unref (conn); ++ soup_server_message_io_http2_unprotect (io); + return G_SOURCE_CONTINUE; + } + +- if (soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io) { ++ if (!io->destroyed) { + if (error) + h2_debug (io, NULL, "[SESSION] IO error: %s", error->message); + + g_clear_pointer (&io->write_source, g_source_unref); + + if (error || (!nghttp2_session_want_read (io->session) && !nghttp2_session_want_write (io->session))) +- soup_server_connection_disconnect (conn); ++ soup_server_connection_disconnect (io->conn); + } + + g_clear_error (&error); +- g_object_unref (conn); ++ soup_server_message_io_http2_unprotect (io); + + return G_SOURCE_REMOVE; + } +@@ -390,13 +430,12 @@ static gboolean io_write_idle_cb (SoupServerMessageIOHTTP2* io); + static void + io_try_write (SoupServerMessageIOHTTP2 *io) + { +- SoupServerConnection *conn = io->conn; + GError *error = NULL; + + if (io->write_source) + return; + +- if (io->in_callback && soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io) { ++ if (io->in_callback && !io->destroyed) { + if (!nghttp2_session_want_write (io->session)) + return; + +@@ -416,12 +455,19 @@ io_try_write (SoupServerMessageIOHTTP2 *io) + g_clear_pointer (&io->write_idle_source, g_source_unref); + } + +- g_object_ref (conn); ++ soup_server_message_io_http2_protect (io); ++ ++ while (!error) { ++ if (io->destroyed) ++ break; ++ ++ if (!nghttp2_session_want_write (io->session)) ++ break; + +- while (!error && soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io && !io->in_callback && nghttp2_session_want_write (io->session)) + io_write (io, &error); ++ } + +- if (soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io) { ++ if (!io->destroyed) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + g_clear_error (&error); + io->write_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (io->ostream), NULL); +@@ -434,11 +480,11 @@ io_try_write (SoupServerMessageIOHTTP2 *io) + h2_debug (io, NULL, "[SESSION] IO error: %s", error->message); + + if (error || (!nghttp2_session_want_read (io->session) && !nghttp2_session_want_write (io->session))) +- soup_server_connection_disconnect (conn); ++ soup_server_connection_disconnect (io->conn); + } + + g_clear_error (&error); +- g_object_unref (conn); ++ soup_server_message_io_http2_unprotect (io); + } + + static gboolean +@@ -481,31 +527,37 @@ static gboolean + io_read_ready (GObject *stream, + SoupServerMessageIOHTTP2 *io) + { +- SoupServerConnection *conn = io->conn; + gboolean progress = TRUE; + GError *error = NULL; + +- g_object_ref (conn); ++ soup_server_message_io_http2_protect (io); ++ ++ while (progress) { ++ if (io->destroyed) ++ break; ++ ++ if (!nghttp2_session_want_read (io->session)) ++ break; + +- while (progress && soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io && nghttp2_session_want_read (io->session)) + progress = io_read (io, &error); ++ } + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + g_error_free (error); +- g_object_unref (conn); ++ soup_server_message_io_http2_unprotect (io); + return G_SOURCE_CONTINUE; + } + +- if (soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io) { ++ if (!io->destroyed) { + if (error) + h2_debug (io, NULL, "[SESSION] IO error: %s", error->message); + + if (error || (!nghttp2_session_want_read (io->session) && !nghttp2_session_want_write (io->session))) +- soup_server_connection_disconnect (conn); ++ soup_server_connection_disconnect (io->conn); + } + + g_clear_error (&error); +- g_object_unref (conn); ++ soup_server_message_io_http2_unprotect (io); + + return G_SOURCE_REMOVE; + } +@@ -931,5 +983,10 @@ soup_server_message_io_http2_new (SoupServerConnection *conn, + nghttp2_submit_settings (io->session, NGHTTP2_FLAG_NONE, settings, G_N_ELEMENTS (settings)); + io_try_write (io); + ++#ifdef __clang_analyzer__ ++ // Suppress false positive about io being destroyed here, since at this point we have only ++ // send the initial settings and not callback is called. ++ [[clang::suppress]] ++#endif + return (SoupServerMessageIO *)io; + } +diff --git a/tests/http2-test.c b/tests/http2-test.c +index dafcb95a..ee15e072 100644 +--- a/tests/http2-test.c ++++ b/tests/http2-test.c +@@ -1368,6 +1368,40 @@ do_broken_pseudo_header_test (Test *test, gconstpointer data) + g_uri_unref (uri); + } + ++static void ++disconnect_on_got_headers (SoupServerMessage *msg, gpointer user_data) ++{ ++ GUri *uri; ++ SoupServerConnection *conn; ++ ++ uri = soup_server_message_get_uri (msg); ++ if (!g_str_equal (g_uri_get_path (uri), "/close-on-got-headers")) ++ return; ++ ++ conn = soup_server_message_get_connection (msg); ++ soup_server_connection_disconnect (conn); ++} ++ ++static void ++do_server_disconnect_on_got_headers_test (Test *test, gconstpointer data) ++{ ++ SoupMessage *msg; ++ GUri *uri; ++ GBytes *response; ++ GError *error = NULL; ++ ++ uri = g_uri_parse_relative (base_uri, "/close-on-got-headers", SOUP_HTTP_URI_FLAGS, NULL); ++ msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); ++ ++ response = soup_test_session_async_send (test->session, msg, NULL, &error); ++ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT); ++ ++ g_clear_error (&error); ++ g_bytes_unref (response); ++ g_object_unref (msg); ++ g_uri_unref (uri); ++} ++ + static gboolean + unpause_message (SoupServerMessage *msg) + { +@@ -1505,12 +1539,26 @@ server_handler (SoupServer *server, + shutdown (fd, SHUT_WR); + #endif + ++ soup_server_message_set_response (msg, "text/plain", ++ SOUP_MEMORY_STATIC, ++ "Success!", 8); ++ } else if (strcmp (path, "/close-on-got-headers") == 0) { + soup_server_message_set_response (msg, "text/plain", + SOUP_MEMORY_STATIC, + "Success!", 8); + } + } + ++static void ++server_request_started (SoupServer *server, ++ SoupServerMessage *msg, ++ SoupServerConnection *conn, ++ gpointer user_data) ++{ ++ g_signal_connect (msg, "got-headers", ++ G_CALLBACK (disconnect_on_got_headers), NULL); ++} ++ + static gboolean + server_basic_auth_callback (SoupAuthDomain *auth_domain, + SoupServerMessage *msg, +@@ -1537,6 +1585,8 @@ main (int argc, char **argv) + return 0; + + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD | SOUP_TEST_SERVER_HTTP2); ++ g_signal_connect (server, "request-started", ++ G_CALLBACK (server_request_started), NULL); + auth = soup_auth_domain_basic_new ("realm", "http2-test", + "auth-callback", server_basic_auth_callback, + NULL); +@@ -1697,6 +1747,10 @@ main (int argc, char **argv) + setup_session, + do_broken_pseudo_header_test, + teardown_session); ++ g_test_add ("/http2/server-disconnect-on-got-headers", Test, NULL, ++ setup_session, ++ do_server_disconnect_on_got_headers_test, ++ teardown_session); + + ret = g_test_run (); + +-- +GitLab + diff --git a/CVE-2026-5119.patch b/CVE-2026-5119.patch new file mode 100644 index 0000000..dd4c237 --- /dev/null +++ b/CVE-2026-5119.patch @@ -0,0 +1,120 @@ +From 1922f08e4cbae4d13430421b1377aad21ad54025 Mon Sep 17 00:00:00 2001 +From: Carlos Garcia Campos +Date: Fri, 27 Feb 2026 12:03:25 +0100 +Subject: [PATCH] cookies: do not send cookies to a HTTP proxy for a HTTPS + request + +Closes #502 +--- + libsoup/cookies/soup-cookie-jar.c | 24 +++++++++++----- + tests/proxy-test.c | 47 +++++++++++++++++++++++++++++++ + 2 files changed, 64 insertions(+), 7 deletions(-) + +diff --git a/libsoup/cookies/soup-cookie-jar.c b/libsoup/cookies/soup-cookie-jar.c +index 51c51d3c..1be56d89 100644 +--- a/libsoup/cookies/soup-cookie-jar.c ++++ b/libsoup/cookies/soup-cookie-jar.c +@@ -890,18 +890,28 @@ process_set_cookie_header (SoupMessage *msg, gpointer user_data) + g_slist_free (new_cookies); + } + ++static gboolean ++allow_cookies_for_request (SoupMessage *msg) ++{ ++ /* Do not send cookies to a HTTP proxy for a HTTPS request */ ++ return soup_message_get_method (msg) != SOUP_METHOD_CONNECT || !soup_connection_is_tunnelled (soup_message_get_connection (msg)); ++} ++ + static void + msg_starting_cb (SoupMessage *msg, gpointer feature) + { + SoupCookieJar *jar = SOUP_COOKIE_JAR (feature); +- GSList *cookies; ++ GSList *cookies = NULL; ++ ++ if (allow_cookies_for_request (msg)) { ++ cookies = soup_cookie_jar_get_cookie_list_with_same_site_info (jar, soup_message_get_uri (msg), ++ soup_message_get_first_party (msg), ++ soup_message_get_site_for_cookies (msg), ++ TRUE, ++ SOUP_METHOD_IS_SAFE (soup_message_get_method (msg)), ++ soup_message_get_is_top_level_navigation (msg)); ++ } + +- cookies = soup_cookie_jar_get_cookie_list_with_same_site_info (jar, soup_message_get_uri (msg), +- soup_message_get_first_party (msg), +- soup_message_get_site_for_cookies (msg), +- TRUE, +- SOUP_METHOD_IS_SAFE (soup_message_get_method (msg)), +- soup_message_get_is_top_level_navigation (msg)); + if (cookies != NULL) { + char *cookie_header = soup_cookies_to_cookie_header (cookies); + soup_message_headers_replace_common (soup_message_get_request_headers (msg), SOUP_HEADER_COOKIE, cookie_header, SOUP_HEADER_VALUE_TRUSTED); +diff --git a/tests/proxy-test.c b/tests/proxy-test.c +index d730c8a7..7d90c053 100644 +--- a/tests/proxy-test.c ++++ b/tests/proxy-test.c +@@ -373,6 +373,52 @@ do_proxy_connect_error_test (gconstpointer data) + soup_test_session_abort_unref (session); + } + ++static void ++connect_message_wrote_headers_cb (SoupMessage *msg, guint *counter) ++{ ++ SoupMessageHeaders *hdrs; ++ ++ *counter += 1; ++ ++ hdrs = soup_message_get_request_headers (msg); ++ if (soup_message_get_method (msg) == SOUP_METHOD_CONNECT) ++ g_assert_null (soup_message_headers_get_one (hdrs, "Cookie")); ++ else ++ g_assert_nonnull (soup_message_headers_get_one (hdrs, "Cookie")); ++} ++ ++static void ++request_queued_cb (SoupSession *session, SoupMessage *msg, guint *counter) ++{ ++ g_signal_connect (msg, "wrote-headers", G_CALLBACK (connect_message_wrote_headers_cb), counter); ++} ++ ++static void ++do_proxy_secure_cookies_test (void) ++{ ++ SoupSession *session; ++ SoupMessage *msg; ++ SoupCookieJar *jar; ++ guint counter = 0; ++ ++ SOUP_TEST_SKIP_IF_NO_APACHE; ++ SOUP_TEST_SKIP_IF_NO_TLS; ++ ++ session = soup_test_session_new ("proxy-resolver", proxy_resolvers[SIMPLE_PROXY], NULL); ++ g_signal_connect (session, "request-queued", G_CALLBACK (request_queued_cb), &counter); ++ ++ soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR); ++ jar = SOUP_COOKIE_JAR (soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR)); ++ ++ msg = soup_message_new (SOUP_METHOD_GET, HTTPS_SERVER); ++ soup_cookie_jar_set_cookie (jar, soup_message_get_uri (msg), "user=password; secure"); ++ soup_test_session_send_message (session, msg); ++ soup_test_assert_message_status (msg, SOUP_STATUS_OK); ++ g_assert_cmpuint (counter, ==, 2); ++ ++ soup_test_session_abort_unref (session); ++} ++ + int + main (int argc, char **argv) + { +@@ -404,6 +450,7 @@ main (int argc, char **argv) + g_test_add_func ("/proxy/redirect", do_proxy_redirect_test); + g_test_add_func ("/proxy/auth-cache", do_proxy_auth_cache_test); + g_test_add_data_func ("/proxy/connect-error", base_https_uri, do_proxy_connect_error_test); ++ g_test_add_func ("/proxy/secure-cookies", do_proxy_secure_cookies_test); + + ret = g_test_run (); + +-- +2.54.0 + diff --git a/http2-body-size-test-timeout.patch b/http2-body-size-test-timeout.patch deleted file mode 100644 index b031b18..0000000 --- a/http2-body-size-test-timeout.patch +++ /dev/null @@ -1,30 +0,0 @@ -From a2eaa4aab2b62976118a4a62f5041eead5c90a02 Mon Sep 17 00:00:00 2001 -From: Michael Catanzaro -Date: Thu, 1 May 2025 08:57:46 -0500 -Subject: [PATCH] Reduce runtime of http2-body-size test - -This test is *really* slow and I think it would be excessive to increase -the test timeout any further, so let's test less data. - -Fixes #444 -Obsoletes: !309 ---- - tests/http2-body-stream-test.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tests/http2-body-stream-test.c b/tests/http2-body-stream-test.c -index 540beb3b..c53f22bc 100644 ---- a/tests/http2-body-stream-test.c -+++ b/tests/http2-body-stream-test.c -@@ -24,7 +24,7 @@ static void - do_large_data_test (void) - { - #define CHUNK_SIZE ((gsize)1024 * 1024 * 512) // 512 MiB --#define TEST_SIZE (CHUNK_SIZE * 20) // 10 GiB -+#define TEST_SIZE (CHUNK_SIZE * 4) // 2 GiB - - GInputStream *stream = soup_body_input_stream_http2_new (); - SoupBodyInputStreamHttp2 *mem_stream = SOUP_BODY_INPUT_STREAM_HTTP2 (stream); --- -GitLab - diff --git a/libsoup3.spec b/libsoup3.spec index 8e4eb2d..9fb58f3 100644 --- a/libsoup3.spec +++ b/libsoup3.spec @@ -1,8 +1,8 @@ %global glib2_version 2.69.1 Name: libsoup3 -Version: 3.6.5 -Release: 3%{?dist}.%{autorelease -n} +Version: 3.6.6 +Release: %autorelease Summary: Soup, an HTTP library implementation License: LGPL-2.0-or-later @@ -12,35 +12,14 @@ Source0: https://download.gnome.org/sources/libsoup/3.6/libsoup-%{version}.tar.x # Downstream patch, needed due to glib2 gnutls-hmac.patch Patch: no-ntlm-in-fips-mode.patch -# https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/426 -Patch: test-timeouts.patch -# https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/454 -Patch: server-test-timeout.patch -# https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/455 -Patch: http2-body-size-test-timeout.patch - -# https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/450 -Patch: CVE-2025-32914.patch -# https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/451 -Patch: CVE-2025-32908.patch # https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/448 (simplified and corrected) Patch: CVE-2025-4035.patch -# https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/463 -Patch: CVE-2025-4948.patch # https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/408 (simplified) Patch: CVE-2025-32049.patch -# https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/452 -Patch: CVE-2025-32907.patch -# https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/473 -Patch: CVE-2025-4945-CVE-2025-11021.patch -# https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/481 -Patch: CVE-2025-12105.patch -# https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/491 -Patch: CVE-2025-14523.patch -# https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/494 -Patch: CVE-2026-0719.patch -# https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/496 -Patch: CVE-2026-1761.patch +# https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/516 +Patch: CVE-2026-5119.patch +# https://gitlab.gnome.org/GNOME/libsoup/-/merge_requests/511 +Patch: CVE-2026-4271.patch BuildRequires: ca-certificates BuildRequires: gcc diff --git a/server-test-timeout.patch b/server-test-timeout.patch deleted file mode 100644 index 9079523..0000000 --- a/server-test-timeout.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 9ff306aa714efd06ceeafacee03298a3665055b1 Mon Sep 17 00:00:00 2001 -From: Michael Catanzaro -Date: Wed, 30 Apr 2025 14:13:41 -0500 -Subject: [PATCH] test-utils: fix deadlock in add_listener_in_thread() - -The mutex is locked in the wrong place here. - -Hopefully fixes #379 ---- - tests/test-utils.c | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git a/tests/test-utils.c b/tests/test-utils.c -index df4cee44..5c1e316c 100644 ---- a/tests/test-utils.c -+++ b/tests/test-utils.c -@@ -607,9 +607,11 @@ static gboolean - add_listener_in_thread (gpointer user_data) - { - AddListenerData *data = user_data; -+ GUri *uri; - -- data->uri = add_listener (data->server, data->scheme, data->host); -+ uri = add_listener (data->server, data->scheme, data->host); - g_mutex_lock (&data->mutex); -+ data->uri = uri; - g_cond_signal (&data->cond); - g_mutex_unlock (&data->mutex); - -@@ -641,9 +643,9 @@ soup_test_server_get_uri (SoupServer *server, - data.host = host; - data.uri = NULL; - -- g_mutex_lock (&data.mutex); - soup_add_completion (context, add_listener_in_thread, &data); - -+ g_mutex_lock (&data.mutex); - while (!data.uri) - g_cond_wait (&data.cond, &data.mutex); - --- -GitLab - diff --git a/sources b/sources index 4d4891f..ae3c209 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (libsoup-3.6.5.tar.xz) = cb44d93b16048d31ae04a8c2416bbe233e0e9bdaf2d9bfe2879260fd3da27e90a0bb05cddbd82cdf81a4a778bd451ad172a14dd31e2fd113c3bbbe13c0029b03 +SHA512 (libsoup-3.6.6.tar.xz) = 4018dc6f9823fd82cde0fecbb50cd1b5dd0ff4963f92f7ea465e67faf81e71580709eec59914ddbdff317963a88e4a8024e60e44087041175bc21e04022857d2 diff --git a/test-timeouts.patch b/test-timeouts.patch deleted file mode 100644 index 33829b1..0000000 --- a/test-timeouts.patch +++ /dev/null @@ -1,103 +0,0 @@ -From e308ed1a277c6b9c35c3f5c0e8e9deb06b2bcf89 Mon Sep 17 00:00:00 2001 -From: Michael Catanzaro -Date: Mon, 13 Jan 2025 11:50:35 -0600 -Subject: [PATCH] Mark several tests as slow - -Let's allow 30 seconds to run most tests, or 5 minutes for tests marked -slow. Most of them are fast on my computer: - - 1/34 cache-test OK 0.08s 7 subtests passed - 2/34 chunk-io-test OK 0.08s 1 subtests passed - 3/34 coding-test OK 0.08s 11 subtests passed - 4/34 continue-test OK 0.08s 12 subtests passed - 5/34 cookies-test OK 0.07s 13 subtests passed - 6/34 date-test OK 0.07s 66 subtests passed - 7/34 header-parsing-test OK 0.07s 8 subtests passed - 8/34 logger-test OK 0.05s 6 subtests passed - 9/34 multipart-test OK 0.05s 4 subtests passed -10/34 multithread-test OK 0.05s 8 subtests passed -11/34 no-ssl-test OK 0.04s 1 subtests passed -12/34 redirect-test OK 0.04s 29 subtests passed -13/34 request-body-test OK 0.04s 18 subtests passed -14/34 samesite-test OK 0.04s 16 subtests passed -15/34 streaming-test OK 0.02s 4 subtests passed -16/34 tld-test OK 0.02s 2 subtests passed -17/34 uri-parsing-test OK 0.02s 4 subtests passed -18/34 sniffing-test OK 0.03s 37 subtests passed -19/34 brotli-decompressor-test OK 0.01s 3 subtests passed -20/34 unix-socket-test OK 0.01s 1 subtests passed -21/34 hsts-db-test OK 0.08s 3 subtests passed -22/34 forms-test OK 0.11s 5 subtests passed -23/34 server-test OK 0.08s 18 subtests passed -24/34 ntlm-test OK 0.10s 21 subtests passed -25/34 ssl-test OK 0.12s 7 subtests passed -26/34 session-test OK 0.16s 6 subtests passed -27/34 misc-test OK 0.22s 18 subtests passed -28/34 context-test OK 0.41s 1 subtests passed -29/34 server-auth-test OK 0.37s 12 subtests passed -30/34 http2-test OK 2.73s 35 subtests passed -31/34 websocket-test OK 3.97s 55 subtests passed -32/34 timeout-test OK 4.08s 4 subtests passed -33/34 http2-body-stream-test OK 8.05s 3 subtests passed -34/34 hsts-test OK 12.12s 25 subtests passed - -A 2 minute timeout is not good enough, so let's use 5 minutes. - -I'm not marking hsts-test as slow because it is fast with the exception -of some hardcoded 2-3 second timeouts that should never cause the total -time to exceed 30s. ---- - .gitlab-ci.yml | 4 ++-- - tests/meson.build | 14 +++++++------- - 2 files changed, 9 insertions(+), 9 deletions(-) - -diff --git a/tests/meson.build b/tests/meson.build -index ee118a01..b4dc8064 100644 ---- a/tests/meson.build -+++ b/tests/meson.build -@@ -87,8 +87,8 @@ tests = [ - {'name': 'date'}, - {'name': 'forms'}, - {'name': 'header-parsing'}, -- {'name': 'http2'}, -- {'name': 'http2-body-stream'}, -+ {'name': 'http2', 'slow': true}, -+ {'name': 'http2-body-stream', 'slow': true}, - {'name': 'hsts'}, - {'name': 'hsts-db'}, - {'name': 'logger'}, -@@ -115,11 +115,12 @@ tests = [ - ] - }, - {'name': 'streaming'}, -- {'name': 'timeout'}, -+ {'name': 'timeout', 'slow': true}, - {'name': 'tld'}, - {'name': 'uri-parsing'}, - {'name': 'websocket', -- 'dependencies': [libz_dep]}, -+ 'dependencies': [libz_dep], -+ 'slow': true}, - ] - - if brotlidec_dep.found() -@@ -219,14 +220,13 @@ foreach test: tests - install_dir : installed_tests_execdir, - install_rpath : abs_installed_tests_execdir, - ) -- # Increase the timeout as on some architectures the tests could be slower -- # than the default 30 seconds. -+ - test(test_name, test_target, - args : ['--debug'], - env : env, - is_parallel : test.get('parallel', true), - depends : test.get('depends', []), -- timeout : 60, -+ timeout : test.get('slow', false) ? 300 : 30, - protocol : 'tap', - ) - endforeach --- -2.49.0 -