summaryrefslogtreecommitdiffstats
path: root/meta
diff options
context:
space:
mode:
authorChangqing Li <changqing.li@windriver.com>2026-04-29 14:15:30 +0800
committerPaul Barker <paul@pbarker.dev>2026-05-12 21:31:34 +0100
commitad166a6de25062ef7297affc245c3dfb375256a2 (patch)
tree26eb31b282a845eccc8d6d911d6f28dfc56d341f /meta
parent16d874ad977f90b3d71fdd574eef746cde56f5fc (diff)
downloadpoky-ad166a6de25062ef7297affc245c3dfb375256a2.tar.gz
libsoup: fix CVE-2025-14523
Refer: https://gitlab.gnome.org/GNOME/libsoup/-/work_items/472 (From OE-Core rev: 277297409dbf0bdb17653419e2d5e4a5ee8f33d5) Signed-off-by: Changqing Li <changqing.li@windriver.com> Signed-off-by: Fabien Thomas <fabien.thomas@smile.fr> Signed-off-by: Paul Barker <paul@pbarker.dev>
Diffstat (limited to 'meta')
-rw-r--r--meta/recipes-support/libsoup/libsoup-3.4.4/CVE-2025-14523.patch715
-rw-r--r--meta/recipes-support/libsoup/libsoup_3.4.4.bb1
2 files changed, 716 insertions, 0 deletions
diff --git a/meta/recipes-support/libsoup/libsoup-3.4.4/CVE-2025-14523.patch b/meta/recipes-support/libsoup/libsoup-3.4.4/CVE-2025-14523.patch
new file mode 100644
index 0000000000..1cf5c9d667
--- /dev/null
+++ b/meta/recipes-support/libsoup/libsoup-3.4.4/CVE-2025-14523.patch
@@ -0,0 +1,715 @@
1From 70123da95418f5d6e00e8ac2d586fb6c5d02cdc6 Mon Sep 17 00:00:00 2001
2From: Michael Catanzaro <mcatanzaro@redhat.com>
3Date: Wed, 7 Jan 2026 14:50:33 -0600
4Subject: [PATCH] Reject duplicate Host headers
5
6RFC 9112 section 3.2 says:
7
8A server MUST respond with a 400 (Bad Request) status code to any
9HTTP/1.1 request message that lacks a Host header field and to any
10request message that contains more than one Host header field line or a
11Host header field with an invalid field value.
12
13In addition to rejecting a duplicate header when parsing headers, also
14reject attempts to add the duplicate header using the
15soup_message_headers_append() API, and add tests for both cases.
16
17These checks will also apply to HTTP/2. I'm not sure whether this is
18actually desired or not, but the header processing code is not aware of
19which HTTP version is in use.
20
21(Note that while SoupMessageHeaders does not require the Host header to
22be present in an HTTP/1.1 request, SoupServer itself does. So we can't
23test the case of missing Host header via the header parsing test, but it
24really is enforced.)
25
26Fixes #472
27
28Upstream-Status: Backport [https://gitlab.gnome.org/GNOME/libsoup/-/commit/aecd8daadc110f8561fb2d6b2806a4cacf2e4c85]
29CVE: CVE-2025-14523
30
31Signed-off-by: Changqing Li <changqing.li@windriver.com>
32---
33 libsoup/soup-headers.c | 3 +-
34 libsoup/soup-message-headers-private.h | 4 +-
35 libsoup/soup-message-headers.c | 80 +++++++------
36 tests/header-parsing-test.c | 148 +++++++++++++++++--------
37 4 files changed, 153 insertions(+), 82 deletions(-)
38
39diff --git a/libsoup/soup-headers.c b/libsoup/soup-headers.c
40index 155c11d..3fec9b3 100644
41--- a/libsoup/soup-headers.c
42+++ b/libsoup/soup-headers.c
43@@ -139,7 +139,8 @@ soup_headers_parse (const char *str, int len, SoupMessageHeaders *dest)
44 for (p = strchr (value, '\r'); p; p = strchr (p, '\r'))
45 *p = ' ';
46
47- soup_message_headers_append_untrusted_data (dest, name, value);
48+ if (!soup_message_headers_append_untrusted_data (dest, name, value))
49+ goto done;
50 }
51 success = TRUE;
52
53diff --git a/libsoup/soup-message-headers-private.h b/libsoup/soup-message-headers-private.h
54index 9815464..770f3ef 100644
55--- a/libsoup/soup-message-headers-private.h
56+++ b/libsoup/soup-message-headers-private.h
57@@ -10,10 +10,10 @@
58
59 G_BEGIN_DECLS
60
61-void soup_message_headers_append_untrusted_data (SoupMessageHeaders *hdrs,
62+gboolean soup_message_headers_append_untrusted_data (SoupMessageHeaders *hdrs,
63 const char *name,
64 const char *value);
65-void soup_message_headers_append_common (SoupMessageHeaders *hdrs,
66+gboolean soup_message_headers_append_common (SoupMessageHeaders *hdrs,
67 SoupHeaderName name,
68 const char *value);
69 const char *soup_message_headers_get_one_common (SoupMessageHeaders *hdrs,
70diff --git a/libsoup/soup-message-headers.c b/libsoup/soup-message-headers.c
71index d69d6e8..ce4b3b3 100644
72--- a/libsoup/soup-message-headers.c
73+++ b/libsoup/soup-message-headers.c
74@@ -267,12 +267,16 @@ soup_message_headers_clean_connection_headers (SoupMessageHeaders *hdrs)
75 soup_header_free_list (tokens);
76 }
77
78-void
79+gboolean
80 soup_message_headers_append_common (SoupMessageHeaders *hdrs,
81 SoupHeaderName name,
82 const char *value)
83 {
84 SoupCommonHeader header;
85+ if (name == SOUP_HEADER_HOST && soup_message_headers_get_one (hdrs, "Host")) {
86+ g_warning ("soup_message_headers_append_common: Rejecting duplicate Host header");
87+ return FALSE;
88+ }
89
90 if (!hdrs->common_headers)
91 hdrs->common_headers = g_array_sized_new (FALSE, FALSE, sizeof (SoupCommonHeader), 6);
92@@ -284,32 +288,18 @@ soup_message_headers_append_common (SoupMessageHeaders *hdrs,
93 g_hash_table_remove (hdrs->common_concat, GUINT_TO_POINTER (header.name));
94
95 soup_message_headers_set (hdrs, name, value);
96+ return TRUE;
97 }
98
99-/**
100- * soup_message_headers_append:
101- * @hdrs: a #SoupMessageHeaders
102- * @name: the header name to add
103- * @value: the new value of @name
104- *
105- * Appends a new header with name @name and value @value to @hdrs.
106- *
107- * (If there is an existing header with name @name, then this creates a second
108- * one, which is only allowed for list-valued headers; see also
109- * [method@MessageHeaders.replace].)
110- *
111- * The caller is expected to make sure that @name and @value are
112- * syntactically correct.
113- **/
114-void
115-soup_message_headers_append (SoupMessageHeaders *hdrs,
116- const char *name, const char *value)
117+static gboolean
118+soup_message_headers_append_internal (SoupMessageHeaders *hdrs,
119+ const char *name, const char *value)
120 {
121 SoupUncommonHeader header;
122 SoupHeaderName header_name;
123
124- g_return_if_fail (name != NULL);
125- g_return_if_fail (value != NULL);
126+ g_return_val_if_fail (name != NULL, FALSE);
127+ g_return_val_if_fail (value != NULL, FALSE);
128
129 /* Setting a syntactically invalid header name or value is
130 * considered to be a programming error. However, it can also
131@@ -317,23 +307,22 @@ soup_message_headers_append (SoupMessageHeaders *hdrs,
132 * compiled with G_DISABLE_CHECKS.
133 */
134 #ifndef G_DISABLE_CHECKS
135- g_return_if_fail (*name && strpbrk (name, " \t\r\n:") == NULL);
136- g_return_if_fail (strpbrk (value, "\r\n") == NULL);
137+ g_return_val_if_fail (*name && strpbrk (name, " \t\r\n:") == NULL, FALSE);
138+ g_return_val_if_fail (strpbrk (value, "\r\n") == NULL, FALSE);
139 #else
140 if (*name && strpbrk (name, " \t\r\n:")) {
141- g_warning ("soup_message_headers_append: Ignoring bad name '%s'", name);
142- return;
143+ g_warning ("soup_message_headers_append: Rejecting bad name '%s'", name);
144+ return FALSE;
145 }
146 if (strpbrk (value, "\r\n")) {
147- g_warning ("soup_message_headers_append: Ignoring bad value '%s'", value);
148- return;
149+ g_warning ("soup_message_headers_append: Rejecting bad value '%s'", value);
150+ return FALSE;
151 }
152 #endif
153
154 header_name = soup_header_name_from_string (name);
155 if (header_name != SOUP_HEADER_UNKNOWN) {
156- soup_message_headers_append_common (hdrs, header_name, value);
157- return;
158+ return soup_message_headers_append_common (hdrs, header_name, value);
159 }
160
161 if (!hdrs->uncommon_headers)
162@@ -344,21 +333,48 @@ soup_message_headers_append (SoupMessageHeaders *hdrs,
163 g_array_append_val (hdrs->uncommon_headers, header);
164 if (hdrs->uncommon_concat)
165 g_hash_table_remove (hdrs->uncommon_concat, header.name);
166+ return TRUE;
167+}
168+
169+/**
170+ * soup_message_headers_append:
171+ * @hdrs: a #SoupMessageHeaders
172+ * @name: the header name to add
173+ * @value: the new value of @name
174+ *
175+ * Appends a new header with name @name and value @value to @hdrs.
176+ *
177+ * (If there is an existing header with name @name, then this creates a second
178+ * one, which is only allowed for list-valued headers; see also
179+ * [method@MessageHeaders.replace].)
180+ *
181+ * The caller is expected to make sure that @name and @value are
182+ * syntactically correct.
183+ **/
184+void
185+soup_message_headers_append (SoupMessageHeaders *hdrs,
186+ const char *name, const char *value)
187+{
188+ soup_message_headers_append_internal (hdrs, name, value);
189 }
190
191 /*
192- * Appends a header value ensuring that it is valid UTF8.
193+ * Appends a header value ensuring that it is valid UTF-8, and also checking the
194+ * return value of soup_message_headers_append_internal() to report whether the
195+ * headers are invalid for various other reasons.
196 */
197-void
198+gboolean
199 soup_message_headers_append_untrusted_data (SoupMessageHeaders *hdrs,
200 const char *name,
201 const char *value)
202 {
203 char *safe_value = g_utf8_make_valid (value, -1);
204 char *safe_name = g_utf8_make_valid (name, -1);
205- soup_message_headers_append (hdrs, safe_name, safe_value);
206+ gboolean result = soup_message_headers_append_internal (hdrs, safe_name, safe_value);
207+
208 g_free (safe_value);
209 g_free (safe_name);
210+ return result;
211 }
212
213 void
214diff --git a/tests/header-parsing-test.c b/tests/header-parsing-test.c
215index 9490559..98a22a4 100644
216--- a/tests/header-parsing-test.c
217+++ b/tests/header-parsing-test.c
218@@ -24,6 +24,7 @@ static struct RequestTest {
219 const char *method, *path;
220 SoupHTTPVersion version;
221 Header headers[10];
222+ GLogLevelFlags log_flags;
223 } reqtests[] = {
224 /**********************/
225 /*** VALID REQUESTS ***/
226@@ -33,7 +34,7 @@ static struct RequestTest {
227 "GET / HTTP/1.0\r\n", -1,
228 SOUP_STATUS_OK,
229 "GET", "/", SOUP_HTTP_1_0,
230- { { NULL } }
231+ { { NULL } }, 0
232 },
233
234 { "Req w/ 1 header", NULL,
235@@ -42,7 +43,7 @@ static struct RequestTest {
236 "GET", "/", SOUP_HTTP_1_1,
237 { { "Host", "example.com" },
238 { NULL }
239- }
240+ }, 0
241 },
242
243 { "Req w/ 1 header, no leading whitespace", NULL,
244@@ -51,7 +52,7 @@ static struct RequestTest {
245 "GET", "/", SOUP_HTTP_1_1,
246 { { "Host", "example.com" },
247 { NULL }
248- }
249+ }, 0
250 },
251
252 { "Req w/ 1 header including trailing whitespace", NULL,
253@@ -60,7 +61,7 @@ static struct RequestTest {
254 "GET", "/", SOUP_HTTP_1_1,
255 { { "Host", "example.com" },
256 { NULL }
257- }
258+ }, 0
259 },
260
261 { "Req w/ 1 header, wrapped", NULL,
262@@ -69,7 +70,7 @@ static struct RequestTest {
263 "GET", "/", SOUP_HTTP_1_1,
264 { { "Foo", "bar baz" },
265 { NULL }
266- }
267+ }, 0
268 },
269
270 { "Req w/ 1 header, wrapped with additional whitespace", NULL,
271@@ -78,7 +79,7 @@ static struct RequestTest {
272 "GET", "/", SOUP_HTTP_1_1,
273 { { "Foo", "bar baz" },
274 { NULL }
275- }
276+ }, 0
277 },
278
279 { "Req w/ 1 header, wrapped with tab", NULL,
280@@ -87,7 +88,7 @@ static struct RequestTest {
281 "GET", "/", SOUP_HTTP_1_1,
282 { { "Foo", "bar baz" },
283 { NULL }
284- }
285+ }, 0
286 },
287
288 { "Req w/ 1 header, wrapped before value", NULL,
289@@ -96,7 +97,7 @@ static struct RequestTest {
290 "GET", "/", SOUP_HTTP_1_1,
291 { { "Foo", "bar baz" },
292 { NULL }
293- }
294+ }, 0
295 },
296
297 { "Req w/ 1 header with empty value", NULL,
298@@ -105,7 +106,7 @@ static struct RequestTest {
299 "GET", "/", SOUP_HTTP_1_1,
300 { { "Host", "" },
301 { NULL }
302- }
303+ }, 0
304 },
305
306 { "Req w/ 2 headers", NULL,
307@@ -115,7 +116,7 @@ static struct RequestTest {
308 { { "Host", "example.com" },
309 { "Connection", "close" },
310 { NULL }
311- }
312+ }, 0
313 },
314
315 { "Req w/ 3 headers", NULL,
316@@ -126,7 +127,7 @@ static struct RequestTest {
317 { "Connection", "close" },
318 { "Blah", "blah" },
319 { NULL }
320- }
321+ }, 0
322 },
323
324 { "Req w/ 3 headers, 1st wrapped", NULL,
325@@ -137,7 +138,7 @@ static struct RequestTest {
326 { "Foo", "bar baz" },
327 { "Blah", "blah" },
328 { NULL }
329- }
330+ }, 0
331 },
332
333 { "Req w/ 3 headers, 2nd wrapped", NULL,
334@@ -148,7 +149,7 @@ static struct RequestTest {
335 { "Blah", "blah" },
336 { "Foo", "bar baz" },
337 { NULL }
338- }
339+ }, 0
340 },
341
342 { "Req w/ 3 headers, 3rd wrapped", NULL,
343@@ -159,7 +160,7 @@ static struct RequestTest {
344 { "Blah", "blah" },
345 { "Foo", "bar baz" },
346 { NULL }
347- }
348+ }, 0
349 },
350
351 { "Req w/ same header multiple times", NULL,
352@@ -168,7 +169,7 @@ static struct RequestTest {
353 "GET", "/", SOUP_HTTP_1_1,
354 { { "Foo", "bar, baz, quux" },
355 { NULL }
356- }
357+ }, 0
358 },
359
360 { "Connection header on HTTP/1.0 message", NULL,
361@@ -178,21 +179,21 @@ static struct RequestTest {
362 { { "Connection", "Bar, Quux" },
363 { "Foo", "bar" },
364 { NULL }
365- }
366+ }, 0
367 },
368
369 { "GET with full URI", "667637",
370 "GET http://example.com HTTP/1.1\r\n", -1,
371 SOUP_STATUS_OK,
372 "GET", "http://example.com", SOUP_HTTP_1_1,
373- { { NULL } }
374+ { { NULL } }, 0
375 },
376
377 { "GET with full URI in upper-case", "667637",
378 "GET HTTP://example.com HTTP/1.1\r\n", -1,
379 SOUP_STATUS_OK,
380 "GET", "HTTP://example.com", SOUP_HTTP_1_1,
381- { { NULL } }
382+ { { NULL } }, 0
383 },
384
385 /* It's better for this to be passed through: this means a SoupServer
386@@ -202,7 +203,7 @@ static struct RequestTest {
387 "GET AbOuT: HTTP/1.1\r\n", -1,
388 SOUP_STATUS_OK,
389 "GET", "AbOuT:", SOUP_HTTP_1_1,
390- { { NULL } }
391+ { { NULL } }, 0
392 },
393
394 /****************************/
395@@ -217,7 +218,7 @@ static struct RequestTest {
396 "GET", "/", SOUP_HTTP_1_1,
397 { { "Host", "example.com" },
398 { NULL }
399- }
400+ }, 0
401 },
402
403 /* RFC 2616 section 3.1 says we MUST accept this */
404@@ -228,7 +229,7 @@ static struct RequestTest {
405 "GET", "/", SOUP_HTTP_1_1,
406 { { "Host", "example.com" },
407 { NULL }
408- }
409+ }, 0
410 },
411
412 /* RFC 2616 section 19.3 says we SHOULD accept these */
413@@ -240,7 +241,7 @@ static struct RequestTest {
414 { { "Host", "example.com" },
415 { "Connection", "close" },
416 { NULL }
417- }
418+ }, 0
419 },
420
421 { "LF instead of CRLF after Request-Line", NULL,
422@@ -249,7 +250,7 @@ static struct RequestTest {
423 "GET", "/", SOUP_HTTP_1_1,
424 { { "Host", "example.com" },
425 { NULL }
426- }
427+ }, 0
428 },
429
430 { "Mixed CRLF/LF", "666316",
431@@ -261,7 +262,7 @@ static struct RequestTest {
432 { "e", "f" },
433 { "g", "h" },
434 { NULL }
435- }
436+ }, 0
437 },
438
439 { "Req w/ incorrect whitespace in Request-Line", NULL,
440@@ -270,7 +271,7 @@ static struct RequestTest {
441 "GET", "/", SOUP_HTTP_1_1,
442 { { "Host", "example.com" },
443 { NULL }
444- }
445+ }, 0
446 },
447
448 { "Req w/ incorrect whitespace after Request-Line", "475169",
449@@ -279,7 +280,7 @@ static struct RequestTest {
450 "GET", "/", SOUP_HTTP_1_1,
451 { { "Host", "example.com" },
452 { NULL }
453- }
454+ }, 0
455 },
456
457 /* If the request/status line is parseable, then we
458@@ -293,7 +294,7 @@ static struct RequestTest {
459 { { "Host", "example.com" },
460 { "Bar", "two" },
461 { NULL }
462- }
463+ }, 0
464 },
465
466 { "First header line is continuation", "666316",
467@@ -303,7 +304,7 @@ static struct RequestTest {
468 { { "Host", "example.com" },
469 { "c", "d" },
470 { NULL }
471- }
472+ }, 0
473 },
474
475 { "Zero-length header name", "666316",
476@@ -313,7 +314,7 @@ static struct RequestTest {
477 { { "a", "b" },
478 { "c", "d" },
479 { NULL }
480- }
481+ }, 0
482 },
483
484 { "CR in header name", "666316",
485@@ -323,7 +324,7 @@ static struct RequestTest {
486 { { "a", "b" },
487 { "c", "d" },
488 { NULL }
489- }
490+ }, 0
491 },
492
493 { "CR in header value", "666316",
494@@ -336,7 +337,7 @@ static struct RequestTest {
495 { "s", "t" }, /* CR at end is ignored */
496 { "c", "d" },
497 { NULL }
498- }
499+ }, 0
500 },
501
502 { "Tab in header name", "666316",
503@@ -351,7 +352,7 @@ static struct RequestTest {
504 { "p", "q z: w" },
505 { "c", "d" },
506 { NULL }
507- }
508+ }, 0
509 },
510
511 { "Tab in header value", "666316",
512@@ -364,7 +365,7 @@ static struct RequestTest {
513 { "z", "w" }, /* trailing tab ignored */
514 { "c", "d" },
515 { NULL }
516- }
517+ }, 0
518 },
519
520 /************************/
521@@ -375,77 +376,77 @@ static struct RequestTest {
522 "GET /\r\n", -1,
523 SOUP_STATUS_BAD_REQUEST,
524 NULL, NULL, -1,
525- { { NULL } }
526+ { { NULL } }, 0
527 },
528
529 { "HTTP 1.2 request (no such thing)", NULL,
530 "GET / HTTP/1.2\r\n", -1,
531 SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
532 NULL, NULL, -1,
533- { { NULL } }
534+ { { NULL } }, 0
535 },
536
537 { "HTTP 2000 request (no such thing)", NULL,
538 "GET / HTTP/2000.0\r\n", -1,
539 SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
540 NULL, NULL, -1,
541- { { NULL } }
542+ { { NULL } }, 0
543 },
544
545 { "Long HTTP version terminating at missing minor version", "https://gitlab.gnome.org/GNOME/libsoup/-/issues/404",
546 unterminated_http_version, sizeof (unterminated_http_version),
547 SOUP_STATUS_BAD_REQUEST,
548 NULL, NULL, -1,
549- { { NULL } }
550+ { { NULL } }, 0
551 },
552
553 { "Non-HTTP request", NULL,
554 "GET / SOUP/1.1\r\nHost: example.com\r\n", -1,
555 SOUP_STATUS_BAD_REQUEST,
556 NULL, NULL, -1,
557- { { NULL } }
558+ { { NULL } }, 0
559 },
560
561 { "Junk after Request-Line", NULL,
562 "GET / HTTP/1.1 blah\r\nHost: example.com\r\n", -1,
563 SOUP_STATUS_BAD_REQUEST,
564 NULL, NULL, -1,
565- { { NULL } }
566+ { { NULL } }, 0
567 },
568
569 { "NUL in Method", NULL,
570 "G\x00T / HTTP/1.1\r\nHost: example.com\r\n", 37,
571 SOUP_STATUS_BAD_REQUEST,
572 NULL, NULL, -1,
573- { { NULL } }
574+ { { NULL } }, 0
575 },
576
577 { "NUL at beginning of Method", "666316",
578 "\x00 / HTTP/1.1\r\nHost: example.com\r\n", 35,
579 SOUP_STATUS_BAD_REQUEST,
580 NULL, NULL, -1,
581- { { NULL } }
582+ { { NULL } }, 0
583 },
584
585 { "NUL in Path", NULL,
586 "GET /\x00 HTTP/1.1\r\nHost: example.com\r\n", 38,
587 SOUP_STATUS_BAD_REQUEST,
588 NULL, NULL, -1,
589- { { NULL } }
590+ { { NULL } }, 0
591 },
592
593 { "No terminating CRLF", NULL,
594 "GET / HTTP/1.1\r\nHost: example.com", -1,
595 SOUP_STATUS_BAD_REQUEST,
596 NULL, NULL, -1,
597- { { NULL } }
598+ { { NULL } }, 0
599 },
600
601 { "Unrecognized expectation", NULL,
602 "GET / HTTP/1.1\r\nHost: example.com\r\nExpect: the-impossible\r\n", -1,
603 SOUP_STATUS_EXPECTATION_FAILED,
604 NULL, NULL, -1,
605- { { NULL } }
606+ { { NULL } }, 0
607 },
608
609 // https://gitlab.gnome.org/GNOME/libsoup/-/issues/377
610@@ -453,21 +454,40 @@ static struct RequestTest {
611 "GET / HTTP/1.1\r\nHost\x00: example.com\r\n", 36,
612 SOUP_STATUS_BAD_REQUEST,
613 NULL, NULL, -1,
614- { { NULL } }
615+ { { NULL } }, 0
616 },
617
618 { "NUL in header value", NULL,
619 "HTTP/1.1 200 OK\r\nFoo: b\x00" "ar\r\n", 28,
620 SOUP_STATUS_BAD_REQUEST,
621 NULL, NULL, -1,
622- { { NULL } }
623+ { { NULL } }, 0
624 },
625
626 { "Only newlines", NULL,
627 only_newlines, sizeof (only_newlines),
628 SOUP_STATUS_BAD_REQUEST,
629 NULL, NULL, -1,
630- { { NULL } }
631+ { { NULL } }, 0
632+ },
633+ { "Duplicate Host headers",
634+ "https://gitlab.gnome.org/GNOME/libsoup/-/issues/472",
635+ "GET / HTTP/1.1\r\nHost: example.com\r\nHost: example.org\r\n",
636+ -1,
637+ SOUP_STATUS_BAD_REQUEST,
638+ NULL, NULL, -1,
639+ { { NULL } },
640+ G_LOG_LEVEL_WARNING
641+ },
642+
643+ { "Duplicate Host headers, case insensitive",
644+ "https://gitlab.gnome.org/GNOME/libsoup/-/issues/472",
645+ "GET / HTTP/1.1\r\nHost: example.com\r\nhost: example.org\r\n",
646+ -1,
647+ SOUP_STATUS_BAD_REQUEST,
648+ NULL, NULL, -1,
649+ { { NULL } },
650+ G_LOG_LEVEL_WARNING
651 }
652 };
653 static const int num_reqtests = G_N_ELEMENTS (reqtests);
654@@ -915,10 +935,17 @@ do_request_tests (void)
655 len = strlen (reqtests[i].request);
656 else
657 len = reqtests[i].length;
658+
659+ if (reqtests[i].log_flags)
660+ g_test_expect_message ("libsoup", reqtests[i].log_flags, "*");
661+
662 status = soup_headers_parse_request (reqtests[i].request, len,
663 headers, &method, &path,
664 &version);
665 g_assert_cmpint (status, ==, reqtests[i].status);
666+ if (reqtests[i].log_flags)
667+ g_test_assert_expected_messages ();
668+
669 if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
670 g_assert_cmpstr (method, ==, reqtests[i].method);
671 g_assert_cmpstr (path, ==, reqtests[i].path);
672@@ -1312,6 +1339,32 @@ do_bad_header_tests (void)
673 soup_message_headers_unref (hdrs);
674 }
675
676+static void
677+do_append_duplicate_host_test (void)
678+{
679+ SoupMessageHeaders *hdrs;
680+ const char *list_value;
681+
682+ hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
683+ soup_message_headers_append (hdrs, "Host", "a");
684+
685+ g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING,
686+ "soup_message_headers_append_common: Rejecting duplicate Host header");
687+ soup_message_headers_append (hdrs, "Host", "b");
688+ g_test_assert_expected_messages ();
689+
690+ /* Case insensitive */
691+ g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING,
692+ "soup_message_headers_append_common: Rejecting duplicate Host header");
693+ soup_message_headers_append (hdrs, "host", "b");
694+ g_test_assert_expected_messages ();
695+
696+ list_value = soup_message_headers_get_list (hdrs, "Host");
697+ g_assert_cmpstr (list_value, ==, "a");
698+
699+ soup_message_headers_unref (hdrs);
700+}
701+
702 int
703 main (int argc, char **argv)
704 {
705@@ -1327,6 +1380,7 @@ main (int argc, char **argv)
706 g_test_add_func ("/header-parsing/content-type", do_content_type_tests);
707 g_test_add_func ("/header-parsing/append-param", do_append_param_tests);
708 g_test_add_func ("/header-parsing/bad", do_bad_header_tests);
709+ g_test_add_func ("/header-parsing/append-duplicate-host", do_append_duplicate_host_test);
710
711 ret = g_test_run ();
712
713--
7142.34.1
715
diff --git a/meta/recipes-support/libsoup/libsoup_3.4.4.bb b/meta/recipes-support/libsoup/libsoup_3.4.4.bb
index c09b06fec2..6be31806f1 100644
--- a/meta/recipes-support/libsoup/libsoup_3.4.4.bb
+++ b/meta/recipes-support/libsoup/libsoup_3.4.4.bb
@@ -46,6 +46,7 @@ SRC_URI = "${GNOME_MIRROR}/libsoup/${SHRT_VER}/libsoup-${PV}.tar.xz \
46 file://CVE-2025-2784.patch \ 46 file://CVE-2025-2784.patch \
47 file://CVE-2025-4945.patch \ 47 file://CVE-2025-4945.patch \
48 file://CVE-2025-12105.patch \ 48 file://CVE-2025-12105.patch \
49 file://CVE-2025-14523.patch \
49" 50"
50SRC_URI[sha256sum] = "291c67725f36ed90ea43efff25064b69c5a2d1981488477c05c481a3b4b0c5aa" 51SRC_URI[sha256sum] = "291c67725f36ed90ea43efff25064b69c5a2d1981488477c05c481a3b4b0c5aa"
51 52