File libsoup-CVE-2026-1467.patch of Package libsoup.42965

Index: libsoup-3.4.4/libsoup/auth/soup-auth.c
===================================================================
--- libsoup-3.4.4.orig/libsoup/auth/soup-auth.c
+++ libsoup-3.4.4/libsoup/auth/soup-auth.c
@@ -647,7 +647,7 @@ GSList *
 soup_auth_get_protection_space (SoupAuth *auth, GUri *source_uri)
 {
 	g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
-        g_return_val_if_fail (SOUP_URI_IS_VALID (source_uri), NULL);
+	g_return_val_if_fail (soup_uri_is_valid (source_uri), NULL);
 
         GUri *source_uri_normalized = soup_uri_copy_with_normalized_flags (source_uri);
 	GSList *ret = SOUP_AUTH_GET_CLASS (auth)->get_protection_space (auth, source_uri_normalized);
Index: libsoup-3.4.4/libsoup/soup-message.c
===================================================================
--- libsoup-3.4.4.orig/libsoup/soup-message.c
+++ libsoup-3.4.4/libsoup/soup-message.c
@@ -976,7 +976,8 @@ soup_message_new (const char *method, co
 	uri = g_uri_parse (uri_string, SOUP_HTTP_URI_FLAGS, NULL);
 	if (!uri)
 		return NULL;
-	if (!SOUP_URI_IS_VALID (uri)) {
+
+	if (!soup_uri_is_valid (uri)) {
 		g_uri_unref (uri);
 		return NULL;
 	}
@@ -999,7 +1000,7 @@ SoupMessage *
 soup_message_new_from_uri (const char *method, GUri *uri)
 {
         g_return_val_if_fail (method != NULL, NULL);
-        g_return_val_if_fail (SOUP_URI_IS_VALID (uri), NULL);
+        g_return_val_if_fail (soup_uri_is_valid (uri), NULL);
 
 	return g_object_new (SOUP_TYPE_MESSAGE,
 			     "method", method,
@@ -1019,7 +1020,7 @@ soup_message_new_from_uri (const char *m
 SoupMessage *
 soup_message_new_options_ping (GUri *base_uri)
 {
-        g_return_val_if_fail (SOUP_URI_IS_VALID (base_uri), NULL);
+        g_return_val_if_fail (soup_uri_is_valid (base_uri), NULL);
 
         return g_object_new (SOUP_TYPE_MESSAGE,
                              "method", SOUP_METHOD_OPTIONS,
@@ -2133,7 +2134,7 @@ soup_message_set_uri (SoupMessage *msg,
         GUri *normalized_uri;
 
 	g_return_if_fail (SOUP_IS_MESSAGE (msg));
-        g_return_if_fail (SOUP_URI_IS_VALID (uri));
+        g_return_if_fail (soup_uri_is_valid (uri));
 
 	priv = soup_message_get_instance_private (msg);
 
Index: libsoup-3.4.4/libsoup/soup-uri-utils-private.h
===================================================================
--- libsoup-3.4.4.orig/libsoup/soup-uri-utils-private.h
+++ libsoup-3.4.4/libsoup/soup-uri-utils-private.h
@@ -10,6 +10,8 @@
 
 G_BEGIN_DECLS
 
+gboolean     soup_uri_is_valid              (GUri       *uri);
+
 gboolean     soup_uri_is_http               (GUri       *uri);
 
 gboolean     soup_uri_is_https              (GUri       *uri);
@@ -28,6 +30,4 @@ GUri        *soup_uri_copy_with_normaliz
 
 char        *soup_uri_get_host_for_headers  (GUri       *uri);
 
-#define SOUP_URI_IS_VALID(x) (x && g_uri_get_host(x) && g_uri_get_host(x)[0])
-
 G_END_DECLS
Index: libsoup-3.4.4/libsoup/soup-uri-utils.c
===================================================================
--- libsoup-3.4.4.orig/libsoup/soup-uri-utils.c
+++ libsoup-3.4.4/libsoup/soup-uri-utils.c
@@ -236,6 +236,68 @@ soup_uri_host_equal (gconstpointer v1, g
 	return g_ascii_strcasecmp (one_host, two_host) == 0;
 }
 
+static gboolean
+is_valid_character_for_host (char c)
+{
+        static const char forbidden_chars[] = { '\t', '\n', '\r', ' ', '#', '/', ':', '<', '>', '?', '@', '[', '\\', ']', '^', '|' };
+        int i;
+
+        for (i = 0; i < G_N_ELEMENTS (forbidden_chars); ++i) {
+                if (c == forbidden_chars[i])
+                        return FALSE;
+        }
+
+        return TRUE;
+}
+
+static gboolean
+is_host_valid (const char* host)
+{
+        int i;
+        gboolean is_valid;
+        char *ascii_host = NULL;
+
+        if (!host || !host[0])
+                return FALSE;
+
+        if (g_hostname_is_non_ascii (host)) {
+
+
+                ascii_host = g_hostname_to_ascii (host);
+                if (!ascii_host)
+                  return FALSE;
+
+                host = ascii_host;
+        }
+
+        if ((g_ascii_isdigit (host[0]) || strchr (host, ':')) && g_hostname_is_ip_address (host)) {
+                g_free (ascii_host);
+                return TRUE;
+        }
+
+        is_valid = TRUE;
+        for (i = 0; host[i] && is_valid; i++)
+                is_valid = is_valid_character_for_host (host[i]);
+
+        g_free (ascii_host);
+
+        return is_valid;
+}
+
+gboolean
+soup_uri_is_valid (GUri *uri)
+{
+        if (!uri)
+                return FALSE;
+
+        if (!is_host_valid (g_uri_get_host (uri)))
+                return FALSE;
+
+        /* FIXME: validate other URI components? */
+
+        return TRUE;
+}
+
 gboolean
 soup_uri_is_https (GUri *uri)
 {
Index: libsoup-3.4.4/tests/uri-parsing-test.c
===================================================================
--- libsoup-3.4.4.orig/tests/uri-parsing-test.c
+++ libsoup-3.4.4/tests/uri-parsing-test.c
@@ -126,6 +126,51 @@ do_copy_tests (void)
         g_uri_unref (uri2);
 }
 
+static struct {
+        const char *scheme;
+        const char *host;
+        const char *as_string;
+        gboolean valid;
+} valid_tests[] = {
+        { "http", "example.com", "http://example.com/", TRUE },
+        { "http", "localhost", "http://localhost/", TRUE },
+        { "http", "127.0.0.1", "http://127.0.0.1/", TRUE },
+        { "http", "::1", "http://[::1]/", TRUE },
+        { "http", "::192.168.0.10", "http://[::192.168.0.10]/", TRUE },
+        { "http", "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210", "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]/", TRUE },
+        { "http", "\xe4\xbe\x8b\xe5\xad\x90.\xe6\xb5\x8b\xe8\xaf\x95", "http://\xe4\xbe\x8b\xe5\xad\x90.\xe6\xb5\x8b\xe8\xaf\x95/", TRUE },
+        { "http", "012x:4567:89AB:cdef:3210:7654:ba98:FeDc", "http://012x:4567:89AB:cdef:3210:7654:ba98:FeDc/", FALSE },
+        { "http", "\texample.com", "http://\texample.com/", FALSE },
+        { "http", "example.com\n", "http://example.com\n/", FALSE },
+        { "http", "\r\nexample.com", "http://\r\nexample.com/", FALSE },
+        { "http", "example .com", "http://example .com/", FALSE },
+        { "http", "example:com", "http://example:com/", FALSE },
+        { "http", "exampl<e>.com", "http://exampl<e>.com/", FALSE },
+        { "http", "exampl[e].com", "http://exampl[e].com/", FALSE },
+        { "http", "exampl^e.com", "http://exampl^e.com/", FALSE },
+        { "http", "examp|e.com", "http://examp|e.com/", FALSE },
+};
+
+static void
+do_valid_tests (void)
+{
+        int i;
+
+        for (i = 0; i < G_N_ELEMENTS (valid_tests); ++i) {
+                GUri *uri;
+                char *uri_str;
+
+                uri = g_uri_build (SOUP_HTTP_URI_FLAGS | G_URI_FLAGS_ENCODED, valid_tests[i].scheme, NULL, valid_tests[i].host, -1, "", NULL, NULL);
+                uri_str = g_uri_to_string (uri);
+
+                g_assert_cmpstr (uri_str, ==, valid_tests[i].as_string);
+                g_assert_true (soup_uri_is_valid (uri) == valid_tests[i].valid);
+
+                g_free (uri_str);
+                g_uri_unref (uri);
+        }
+}
+
 #define CONTENT_TYPE_DEFAULT "text/plain;charset=US-ASCII"
 
 static struct {
@@ -204,6 +249,7 @@ main (int argc, char **argv)
 
 	g_test_add_func ("/uri/equality", do_equality_tests);
 	g_test_add_func ("/uri/copy", do_copy_tests);
+	g_test_add_func ("/uri/valid", do_valid_tests);
         g_test_add_func ("/data", do_data_uri_tests);
         g_test_add_func ("/path_and_query", do_path_and_query_tests);
 
openSUSE Build Service is sponsored by