Bug#1109147: bookworm-pu: package libsoup3/3.2.3-0+deb12u1
On Sat, 12 Jul 2025 at 15:27:32 +0100, Simon McVittie wrote:
[x] attach debdiff against the package in testing
Of course this should have said "against the package in stable"
(copypasta error), and it clearly isn't my day for remembering to attach
attachments. Sorry. Now attached.
smcv
debdiff libsoup3_3.2.2-2.dsc libsoup3_3.2.3-0+deb12u1.dsc | filterdiff -p1 -x'debian/patches/*.patch'
diff -Nru libsoup3-3.2.2/debian/changelog libsoup3-3.2.3/debian/changelog
--- libsoup3-3.2.2/debian/changelog 2023-03-01 19:57:12.000000000 +0000
+++ libsoup3-3.2.3/debian/changelog 2025-07-12 14:39:06.000000000 +0100
@@ -1,3 +1,88 @@
+libsoup3 (3.2.3-0+deb12u1) bookworm; urgency=medium
+
+ * Team upload
+
+ [ Jeremy Bícha ]
+ * d/control{,.in}: Add Build-Depends: ca-certificates for build-time tests
+ (Closes: #1064744, #1054962)
+
+ [ Simon McVittie ]
+ * Re-export patch series (no functional changes)
+ * New upstream old-stable release 3.2.3
+ - Fix a buffer overrun if asked to parse non-UTF-8 headers. It is
+ believed that this cannot happen on the client side, but it can
+ happen in SoupServer. (CVE-2024-52531, Closes: #1087417)
+ - Avoid an infinite loop in WebSocket processing which can cause a denial
+ of service via resource exhaustion (CVE-2024-52532, Closes: #1087416)
+ - Fix denial of service (crash) when parsing invalid data URLs
+ (CVE-2025-32051)
+ - Fix heap overflows during content sniffing
+ (CVE-2025-32052, libsoup3 equivalent of #1102214)
+ (CVE-2025-32053, libsoup3 equivalent of #1102215)
+ - Fix an integer overflow during parameter serialization
+ (CVE-2025-32050, libsoup3 equivalent of #1102212)
+ * Fix a regression introduced in 3.2.3 by backporting its fixes from
+ 3.6.5:
+ - d/p/sniffer-Fix-potential-overflow.patch,
+ d/p/sniffer-Add-better-coverage-of-skip_insignificant_space.patch:
+ Fix more heap buffer overflows during content sniffing
+ (CVE-2025-2784; libsoup3 equivalent of #1102208)
+ - d/source/include-binaries: Configure dpkg to accept non-text diffs
+ in test data for CVE-2025-2784
+ * d/p/server-Add-note-about-recommended-usage.patch:
+ Update documentation to indicate the level of security support for
+ the server side.
+ Upstream clarified the documentation in 3.6.1 to state that SoupServer
+ is not intended to be exposed to untrusted clients.
+ (Related to CVE-2024-52531, CVE-2024-52532)
+ * d/p/tests-Add-test-for-passing-invalid-UTF-8-to-soup_header_p.patch:
+ Add test coverage related to CVE-2024-52531
+ * Backport additional CVE fixes from upstream release 3.5.2:
+ - d/p/headers-Strictly-don-t-allow-NUL-bytes.patch:
+ Reject HTTP headers if they contain NUL bytes
+ (CVE-2024-52530, libsoup3 equivalent of #1088812)
+ * Backport additional CVE fixes from upstream release 3.6.2:
+ - d/p/content-sniffer-Handle-sniffing-resource-shorter-than-4-b.patch:
+ Fix denial of service when sniffing type of a short resource
+ (CVE-2025-32909, libsoup3 equivalent of #1103517)
+ - d/p/auth-digest-Handle-missing-realm-in-authenticate-header.patch,
+ d/p/auth-digest-Handle-missing-nonce.patch,
+ d/p/auth-digest-Fix-leak.patch:
+ Fix denial of service (crash) during client-side authentication
+ (CVE-2025-32910, libsoup3 equivalent of #1103516)
+ - d/p/soup_message_headers_get_content_disposition-Fix-NULL-der.patch,
+ d/p/soup_message_headers_get_content_disposition-strdup-trunc.patch:
+ Fix memory management of message headers.
+ (CVE-2025-32911, CVE-2025-32913; libsoup3 equivalent of #1103515)
+ - d/p/soup_header_parse_quality_list-Fix-leak.patch:
+ Fix a memory leak (slow denial of service) in quality list parsing
+ (CVE-2025-46420, libsoup3 equivalent of #1104055)
+ * Backport additional CVE fixes from upstream release 3.6.5:
+ - d/p/auth-digest-Handle-missing-nonce-1.patch,
+ d/p/digest-auth-Handle-NULL-nonce.patch:
+ Fix additional denial of service issues related to CVE-2025-32910
+ (CVE-2025-32912, libsoup3 equivalent of #1103516)
+ - d/p/headers-Handle-parsing-edge-case.patch,
+ d/p/headers-Handle-parsing-only-newlines.patch:
+ Fix denial of service (crash) in http server header parsing
+ (CVE-2025-32906, libsoup3 equivalent of #1103521)
+ - d/p/session-Strip-authentication-credentails-on-cross-origin-.patch:
+ Fix credentials disclosure on cross-origin redirect
+ (CVE-2025-46421, libsoup3 equivalent of #110405)
+ * d/control: libsoup-3.0-tests Depends on ca-certificates
+ (Equivalent of #1054962, #1064744 for autopkgtests)
+ * d/p/connection-manager-don-t-crash-if-connection-outlives-its.patch:
+ Add patch from upstream fixing a use-after-free during disconnection.
+ In particular this resolves a hang during gnome-calculator startup,
+ when it downloads currency conversion data.
+ (Closes: #1077962, #1052551, #1098315, #1099119, #1100509, #1104456,
+ #1100541, #1101922, #1102471, #1059773)
+ * d/p/connection-auth-don-t-crash-if-connection-outlives-the-au.patch:
+ Add patch from upstream fixing another use-after-free during disconnect.
+ (Related to #1077962, etc.)
+
+ -- Simon McVittie <smcv@debian.org> Sat, 12 Jul 2025 14:39:06 +0100
+
libsoup3 (3.2.2-2) unstable; urgency=medium
* Temporarily disable sysprof on hppa
diff -Nru libsoup3-3.2.2/debian/control libsoup3-3.2.3/debian/control
--- libsoup3-3.2.2/debian/control 2023-03-01 19:57:12.000000000 +0000
+++ libsoup3-3.2.3/debian/control 2025-07-12 14:39:06.000000000 +0100
@@ -8,6 +8,7 @@
Maintainer: Debian GNOME Maintainers <pkg-gnome-maintainers@lists.alioth.debian.org>
Uploaders: Jeremy Bicha <jbicha@ubuntu.com>
Build-Depends: apache2 (>= 2.4) <!nocheck> <!noinsttest>,
+ ca-certificates <!nocheck> <!noinsttest>,
curl <!nocheck> <!noinsttest>,
debhelper-compat (= 13),
dh-sequence-gir,
@@ -170,6 +171,7 @@
Section: misc
Architecture: any
Depends: apache2 (>= 2.4),
+ ca-certificates,
curl,
libapache2-mod-php (<< 2:9),
libapache2-mod-php (>= 2:7),
diff -Nru libsoup3-3.2.2/debian/control.in libsoup3-3.2.3/debian/control.in
--- libsoup3-3.2.2/debian/control.in 2023-03-01 19:57:12.000000000 +0000
+++ libsoup3-3.2.3/debian/control.in 2025-07-12 14:39:06.000000000 +0100
@@ -4,6 +4,7 @@
Maintainer: Debian GNOME Maintainers <pkg-gnome-maintainers@lists.alioth.debian.org>
Uploaders: @GNOME_TEAM@
Build-Depends: apache2 (>= 2.4) <!nocheck> <!noinsttest>,
+ ca-certificates <!nocheck> <!noinsttest>,
curl <!nocheck> <!noinsttest>,
debhelper-compat (= 13),
dh-sequence-gir,
@@ -166,6 +167,7 @@
Section: misc
Architecture: any
Depends: apache2 (>= 2.4),
+ ca-certificates,
curl,
libapache2-mod-php (<< 2:9),
libapache2-mod-php (>= 2:7),
diff -Nru libsoup3-3.2.2/debian/patches/series libsoup3-3.2.3/debian/patches/series
--- libsoup3-3.2.2/debian/patches/series 2023-03-01 19:57:12.000000000 +0000
+++ libsoup3-3.2.3/debian/patches/series 2025-07-12 14:39:06.000000000 +0100
@@ -2,3 +2,22 @@
Record-Apache-error-log-for-unit-tests-and-show-it-during.patch
test-utils-Add-more-debug-for-starting-stopping-Apache.patch
tests-extend-timeout-for-http2-body-stream-test.patch
+connection-manager-don-t-crash-if-connection-outlives-its.patch
+connection-auth-don-t-crash-if-connection-outlives-the-au.patch
+headers-Strictly-don-t-allow-NUL-bytes.patch
+tests-Add-test-for-passing-invalid-UTF-8-to-soup_header_p.patch
+server-Add-note-about-recommended-usage.patch
+sniffer-Fix-potential-overflow.patch
+sniffer-Add-better-coverage-of-skip_insignificant_space.patch
+content-sniffer-Handle-sniffing-resource-shorter-than-4-b.patch
+auth-digest-Handle-missing-realm-in-authenticate-header.patch
+auth-digest-Handle-missing-nonce.patch
+auth-digest-Fix-leak.patch
+soup_message_headers_get_content_disposition-Fix-NULL-der.patch
+soup_message_headers_get_content_disposition-strdup-trunc.patch
+soup_header_parse_quality_list-Fix-leak.patch
+auth-digest-Handle-missing-nonce-1.patch
+digest-auth-Handle-NULL-nonce.patch
+headers-Handle-parsing-edge-case.patch
+headers-Handle-parsing-only-newlines.patch
+session-Strip-authentication-credentails-on-cross-origin-.patch
Binary files /tmp/gXFt63eDKs/libsoup3-3.2.2/debian/patches/sniffer-Add-better-coverage-of-skip_insignificant_space.patch and /tmp/Sh13hCjkhM/libsoup3-3.2.3/debian/patches/sniffer-Add-better-coverage-of-skip_insignificant_space.patch differ
Binary files /tmp/gXFt63eDKs/libsoup3-3.2.2/debian/patches/sniffer-Fix-potential-overflow.patch and /tmp/Sh13hCjkhM/libsoup3-3.2.3/debian/patches/sniffer-Fix-potential-overflow.patch differ
diff -Nru libsoup3-3.2.2/debian/source/include-binaries libsoup3-3.2.3/debian/source/include-binaries
--- libsoup3-3.2.2/debian/source/include-binaries 1970-01-01 01:00:00.000000000 +0100
+++ libsoup3-3.2.3/debian/source/include-binaries 2025-07-12 14:39:06.000000000 +0100
@@ -0,0 +1,3 @@
+debian/patches/sniffer-Fix-potential-overflow.patch
+debian/patches/sniffer-Add-better-coverage-of-skip_insignificant_space.patch
+
diff -Nru libsoup3-3.2.2/debian/watch libsoup3-3.2.3/debian/watch
--- libsoup3-3.2.2/debian/watch 2023-03-01 19:57:12.000000000 +0000
+++ libsoup3-3.2.3/debian/watch 2025-07-12 14:39:06.000000000 +0100
@@ -1,4 +1,4 @@
version=4
opts="searchmode=plain, uversionmangle=s/\.(alpha|beta|rc)/~$1/, downloadurlmangle=s|cache.json||" \
https://download.gnome.org/sources/libsoup/cache.json \
- 3[\d.]+[02468]/libsoup-([\d.]+)@ARCHIVE_EXT@
+ 3.2/libsoup-([\d.]+)@ARCHIVE_EXT@
diff -Nru libsoup3-3.2.2/libsoup/auth/soup-auth-digest.c libsoup3-3.2.3/libsoup/auth/soup-auth-digest.c
--- libsoup3-3.2.2/libsoup/auth/soup-auth-digest.c 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/libsoup/auth/soup-auth-digest.c 2025-07-12 14:43:24.000000000 +0100
@@ -72,6 +72,7 @@
g_free (priv->nonce);
g_free (priv->domain);
g_free (priv->cnonce);
+ g_free (priv->opaque);
memset (priv->hex_urp, 0, sizeof (priv->hex_urp));
memset (priv->hex_a1, 0, sizeof (priv->hex_a1));
@@ -139,6 +140,19 @@
}
static gboolean
+validate_params (SoupAuthDigest *auth_digest)
+{
+ SoupAuthDigestPrivate *priv = soup_auth_digest_get_instance_private (auth_digest);
+
+ if (priv->qop || priv->algorithm == SOUP_AUTH_DIGEST_ALGORITHM_MD5_SESS) {
+ if (!priv->nonce)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
soup_auth_digest_update (SoupAuth *auth, SoupMessage *msg,
GHashTable *auth_params)
{
@@ -148,6 +162,9 @@
guint qop_options;
gboolean ok = TRUE;
+ if (!soup_auth_get_realm (auth) || !g_hash_table_lookup (auth_params, "nonce"))
+ return FALSE;
+
g_free (priv->domain);
g_free (priv->nonce);
g_free (priv->opaque);
@@ -172,16 +189,21 @@
if (priv->algorithm == -1)
ok = FALSE;
- stale = g_hash_table_lookup (auth_params, "stale");
- if (stale && !g_ascii_strcasecmp (stale, "TRUE") && *priv->hex_urp)
- recompute_hex_a1 (priv);
- else {
- g_free (priv->user);
- priv->user = NULL;
- g_free (priv->cnonce);
- priv->cnonce = NULL;
- memset (priv->hex_urp, 0, sizeof (priv->hex_urp));
- memset (priv->hex_a1, 0, sizeof (priv->hex_a1));
+ if (!validate_params (auth_digest))
+ ok = FALSE;
+
+ if (ok) {
+ stale = g_hash_table_lookup (auth_params, "stale");
+ if (stale && !g_ascii_strcasecmp (stale, "TRUE") && *priv->hex_urp)
+ recompute_hex_a1 (priv);
+ else {
+ g_free (priv->user);
+ priv->user = NULL;
+ g_free (priv->cnonce);
+ priv->cnonce = NULL;
+ memset (priv->hex_urp, 0, sizeof (priv->hex_urp));
+ memset (priv->hex_a1, 0, sizeof (priv->hex_a1));
+ }
}
return ok;
@@ -273,6 +295,8 @@
/* In MD5-sess, A1 is hex_urp:nonce:cnonce */
+ g_assert (nonce && cnonce);
+
checksum = g_checksum_new (G_CHECKSUM_MD5);
g_checksum_update (checksum, (guchar *)hex_urp, strlen (hex_urp));
g_checksum_update (checksum, (guchar *)":", 1);
@@ -363,6 +387,8 @@
if (qop) {
char tmp[9];
+ g_assert (cnonce);
+
g_snprintf (tmp, 9, "%.8x", nc);
g_checksum_update (checksum, (guchar *)tmp, strlen (tmp));
g_checksum_update (checksum, (guchar *)":", 1);
@@ -426,6 +452,9 @@
g_return_val_if_fail (uri != NULL, NULL);
url = soup_uri_get_path_and_query (uri);
+ g_assert (priv->nonce);
+ g_assert (!priv->qop || priv->cnonce);
+
soup_auth_digest_compute_response (soup_message_get_method (msg), url, priv->hex_a1,
priv->qop, priv->nonce,
priv->cnonce, priv->nc,
diff -Nru libsoup3-3.2.2/libsoup/auth/soup-connection-auth.c libsoup3-3.2.3/libsoup/auth/soup-connection-auth.c
--- libsoup3-3.2.2/libsoup/auth/soup-connection-auth.c 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/libsoup/auth/soup-connection-auth.c 2025-07-12 14:43:24.000000000 +0100
@@ -103,8 +103,8 @@
if (!state) {
state = SOUP_CONNECTION_AUTH_GET_CLASS (auth)->create_connection_state (auth);
if (conn) {
- g_signal_connect (conn, "disconnected",
- G_CALLBACK (connection_disconnected), auth);
+ g_signal_connect_object (conn, "disconnected",
+ G_CALLBACK (connection_disconnected), auth, 0);
}
g_hash_table_insert (priv->conns, conn, state);
diff -Nru libsoup3-3.2.2/libsoup/content-sniffer/soup-content-sniffer.c libsoup3-3.2.3/libsoup/content-sniffer/soup-content-sniffer.c
--- libsoup3-3.2.2/libsoup/content-sniffer/soup-content-sniffer.c 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/libsoup/content-sniffer/soup-content-sniffer.c 2025-07-12 14:43:24.000000000 +0100
@@ -234,9 +234,14 @@
gsize resource_length;
const char *resource = g_bytes_get_data (buffer, &resource_length);
resource_length = MIN (512, resource_length);
- guint32 box_size = *((guint32*)resource);
+ guint32 box_size;
guint i;
+ if (resource_length < sizeof (guint32))
+ return FALSE;
+
+ box_size = *((guint32*)resource);
+
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
box_size = ((box_size >> 24) |
((box_size << 8) & 0x00FF0000) |
@@ -515,7 +520,7 @@
guint index_pattern = 0;
gboolean skip_row = FALSE;
- while ((index_stream < resource_length) &&
+ while ((index_stream < resource_length - 1) &&
(index_pattern <= type_row->pattern_length)) {
/* Skip insignificant white space ("WS" in the spec) */
if (type_row->pattern[index_pattern] == ' ') {
@@ -624,15 +629,18 @@
}
static gboolean
-skip_insignificant_space (const char *resource, int *pos, int resource_length)
+skip_insignificant_space (const char *resource, gsize *pos, gsize resource_length)
{
+ if (*pos >= resource_length)
+ return TRUE;
+
while ((resource[*pos] == '\x09') ||
(resource[*pos] == '\x20') ||
(resource[*pos] == '\x0A') ||
(resource[*pos] == '\x0D')) {
*pos = *pos + 1;
- if (*pos > resource_length)
+ if (*pos >= resource_length)
return TRUE;
}
@@ -645,7 +653,7 @@
gsize resource_length;
const char *resource = g_bytes_get_data (buffer, &resource_length);
resource_length = MIN (512, resource_length);
- int pos = 0;
+ gsize pos = 0;
if (resource_length < 3)
goto text_html;
@@ -655,9 +663,6 @@
pos = 3;
look_for_tag:
- if (pos > resource_length)
- goto text_html;
-
if (skip_insignificant_space (resource, &pos, resource_length))
goto text_html;
@@ -695,7 +700,7 @@
do {
pos++;
- if (pos > resource_length)
+ if ((pos + 1) > resource_length)
goto text_html;
} while (resource[pos] != '>');
diff -Nru libsoup3-3.2.2/libsoup/server/soup-server.c libsoup3-3.2.3/libsoup/server/soup-server.c
--- libsoup3-3.2.2/libsoup/server/soup-server.c 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/libsoup/server/soup-server.c 2025-07-12 14:43:24.000000000 +0100
@@ -28,9 +28,11 @@
/**
* SoupServer:
*
- * A HTTP server.
- *
- * #SoupServer implements a simple HTTP server.
+ * #SoupServer provides a basic implementation of an HTTP server. The
+ * recommended usage of this server is for internal use, tasks like
+ * a mock server for tests, a private service for IPC, etc. It is not
+ * recommended to be exposed to untrusted clients as it may be vulnerable
+ * to denial of service attacks or other exploits.
*
* To begin, create a server using [ctor@Server.new]. Add at least one
* handler by calling [method@Server.add_handler] or
diff -Nru libsoup3-3.2.2/libsoup/soup-connection-manager.c libsoup3-3.2.3/libsoup/soup-connection-manager.c
--- libsoup3-3.2.2/libsoup/soup-connection-manager.c 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/libsoup/soup-connection-manager.c 2025-07-12 14:43:24.000000000 +0100
@@ -183,6 +183,26 @@
return host;
}
+static void
+soup_connection_manager_drop_connection (SoupConnectionManager *manager,
+ SoupConnection *conn)
+{
+ g_signal_handlers_disconnect_by_data (conn, manager);
+ manager->num_conns--;
+ g_object_unref (conn);
+
+ g_cond_broadcast (&manager->cond);
+}
+
+static void
+remove_connection (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ SoupConnectionManager *manager = user_data;
+ soup_connection_manager_drop_connection (manager, key);
+}
+
SoupConnectionManager *
soup_connection_manager_new (SoupSession *session,
guint max_conns,
@@ -212,6 +232,9 @@
void
soup_connection_manager_free (SoupConnectionManager *manager)
{
+ g_hash_table_foreach (manager->conns, remove_connection, manager);
+ g_assert (manager->num_conns == 0);
+
g_clear_object (&manager->remote_connectable);
g_hash_table_destroy (manager->http_hosts);
g_hash_table_destroy (manager->https_hosts);
@@ -271,17 +294,6 @@
}
static void
-soup_connection_manager_drop_connection (SoupConnectionManager *manager,
- SoupConnection *conn)
-{
- g_signal_handlers_disconnect_by_data (conn, manager);
- manager->num_conns--;
- g_object_unref (conn);
-
- g_cond_broadcast (&manager->cond);
-}
-
-static void
soup_connection_list_disconnect_all (GList *conns)
{
GList *c;
diff -Nru libsoup3-3.2.2/libsoup/soup-headers.c libsoup3-3.2.3/libsoup/soup-headers.c
--- libsoup3-3.2.2/libsoup/soup-headers.c 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/libsoup/soup-headers.c 2025-07-12 14:43:24.000000000 +0100
@@ -51,13 +51,14 @@
* ignorable trailing whitespace.
*/
+ /* No '\0's are allowed */
+ if (memchr (str, '\0', len))
+ return FALSE;
+
/* Skip over the Request-Line / Status-Line */
headers_start = memchr (str, '\n', len);
if (!headers_start)
return FALSE;
- /* No '\0's in the Request-Line / Status-Line */
- if (memchr (str, '\0', headers_start - str))
- return FALSE;
/* We work on a copy of the headers, which we can write '\0's
* into, so that we don't have to individually g_strndup and
@@ -69,14 +70,6 @@
headers_copy[copy_len] = '\0';
value_end = headers_copy;
- /* There shouldn't be any '\0's in the headers already, but
- * this is the web we're talking about.
- */
- while ((p = memchr (headers_copy, '\0', copy_len))) {
- memmove (p, p + 1, copy_len - (p - headers_copy));
- copy_len--;
- }
-
while (*(value_end + 1)) {
name = value_end + 1;
name_end = strchr (name, ':');
@@ -193,7 +186,7 @@
/* RFC 2616 4.1 "servers SHOULD ignore any empty line(s)
* received where a Request-Line is expected."
*/
- while ((*str == '\r' || *str == '\n') && len > 0) {
+ while (len > 0 && (*str == '\r' || *str == '\n')) {
str++;
len--;
}
@@ -232,7 +225,7 @@
!g_ascii_isdigit (version[5]))
return SOUP_STATUS_BAD_REQUEST;
major_version = strtoul (version + 5, &p, 10);
- if (*p != '.' || !g_ascii_isdigit (p[1]))
+ if (p + 1 >= str + len || *p != '.' || !g_ascii_isdigit (p[1]))
return SOUP_STATUS_BAD_REQUEST;
minor_version = strtoul (p + 1, &p, 10);
version_end = p;
@@ -378,7 +371,7 @@
* after a response, which we then see prepended to the next
* response on that connection.
*/
- while ((*str == '\r' || *str == '\n') && len > 0) {
+ while (len > 0 && (*str == '\r' || *str == '\n')) {
str++;
len--;
}
@@ -537,7 +530,7 @@
GSList *unsorted;
QualityItem *array;
GSList *sorted, *iter;
- char *item, *semi;
+ char *semi;
const char *param, *equal, *value;
double qval;
int n;
@@ -550,9 +543,8 @@
unsorted = soup_header_parse_list (header);
array = g_new0 (QualityItem, g_slist_length (unsorted));
for (iter = unsorted, n = 0; iter; iter = iter->next) {
- item = iter->data;
qval = 1.0;
- for (semi = strchr (item, ';'); semi; semi = strchr (semi + 1, ';')) {
+ for (semi = strchr (iter->data, ';'); semi; semi = strchr (semi + 1, ';')) {
param = skip_lws (semi + 1);
if (*param != 'q')
continue;
@@ -584,15 +576,15 @@
if (qval == 0.0) {
if (unacceptable) {
*unacceptable = g_slist_prepend (*unacceptable,
- item);
+ g_steal_pointer (&iter->data));
}
} else {
- array[n].item = item;
+ array[n].item = g_steal_pointer (&iter->data);
array[n].qval = qval;
n++;
}
}
- g_slist_free (unsorted);
+ g_slist_free_full (unsorted, g_free);
qsort (array, n, sizeof (QualityItem), sort_by_qval);
sorted = NULL;
@@ -653,8 +645,9 @@
}
static void
-decode_quoted_string (char *quoted_string)
+decode_quoted_string_inplace (GString *quoted_gstring)
{
+ char *quoted_string = quoted_gstring->str;
char *src, *dst;
src = quoted_string + 1;
@@ -668,10 +661,11 @@
}
static gboolean
-decode_rfc5987 (char *encoded_string)
+decode_rfc5987_inplace (GString *encoded_gstring)
{
char *q, *decoded;
gboolean iso_8859_1 = FALSE;
+ const char *encoded_string = encoded_gstring->str;
q = strchr (encoded_string, '\'');
if (!q)
@@ -703,14 +697,7 @@
decoded = utf8;
}
- /* If encoded_string was UTF-8, then each 3-character %-escape
- * will be converted to a single byte, and so decoded is
- * shorter than encoded_string. If encoded_string was
- * iso-8859-1, then each 3-character %-escape will be
- * converted into at most 2 bytes in UTF-8, and so it's still
- * shorter.
- */
- strcpy (encoded_string, decoded);
+ g_string_assign (encoded_gstring, decoded);
g_free (decoded);
return TRUE;
}
@@ -720,15 +707,17 @@
{
GHashTable *params;
GSList *list, *iter;
- char *item, *eq, *name_end, *value;
- gboolean override, duplicated;
params = g_hash_table_new_full (soup_str_case_hash,
soup_str_case_equal,
- g_free, NULL);
+ g_free, g_free);
list = parse_list (header, delim);
for (iter = list; iter; iter = iter->next) {
+ char *item, *eq, *name_end;
+ gboolean override, duplicated;
+ GString *parsed_value = NULL;
+
item = iter->data;
override = FALSE;
@@ -743,19 +732,19 @@
*name_end = '\0';
- value = (char *)skip_lws (eq + 1);
+ parsed_value = g_string_new ((char *)skip_lws (eq + 1));
if (name_end[-1] == '*' && name_end > item + 1) {
name_end[-1] = '\0';
- if (!decode_rfc5987 (value)) {
+ if (!decode_rfc5987_inplace (parsed_value)) {
+ g_string_free (parsed_value, TRUE);
g_free (item);
continue;
}
override = TRUE;
- } else if (*value == '"')
- decode_quoted_string (value);
- } else
- value = NULL;
+ } else if (parsed_value->str[0] == '"')
+ decode_quoted_string_inplace (parsed_value);
+ }
duplicated = g_hash_table_lookup_extended (params, item, NULL, NULL);
@@ -763,11 +752,16 @@
soup_header_free_param_list (params);
params = NULL;
g_slist_foreach (iter, (GFunc)g_free, NULL);
+ if (parsed_value)
+ g_string_free (parsed_value, TRUE);
break;
- } else if (override || !duplicated)
- g_hash_table_replace (params, item, value);
- else
+ } else if (override || !duplicated) {
+ g_hash_table_replace (params, item, parsed_value ? g_string_free (parsed_value, FALSE) : NULL);
+ } else {
+ if (parsed_value)
+ g_string_free (parsed_value, TRUE);
g_free (item);
+ }
}
g_slist_free (list);
@@ -912,7 +906,7 @@
const char *name,
const char *value)
{
- int len;
+ gsize len;
g_string_append (string, name);
g_string_append (string, "=\"");
diff -Nru libsoup3-3.2.2/libsoup/soup-message-headers.c libsoup3-3.2.3/libsoup/soup-message-headers.c
--- libsoup3-3.2.2/libsoup/soup-message-headers.c 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/libsoup/soup-message-headers.c 2025-07-12 14:43:24.000000000 +0100
@@ -1608,10 +1608,15 @@
*/
if (params && g_hash_table_lookup_extended (*params, "filename",
&orig_key, &orig_value)) {
- char *filename = strrchr (orig_value, '/');
+ if (orig_value) {
+ char *filename = strrchr (orig_value, '/');
- if (filename)
- g_hash_table_insert (*params, g_strdup (orig_key), filename + 1);
+ if (filename)
+ g_hash_table_insert (*params, g_strdup (orig_key), g_strdup (filename + 1));
+ } else {
+ /* filename with no value isn't valid. */
+ g_hash_table_remove (*params, "filename");
+ }
}
return TRUE;
}
diff -Nru libsoup3-3.2.2/libsoup/soup-session.c libsoup3-3.2.3/libsoup/soup-session.c
--- libsoup3-3.2.2/libsoup/soup-session.c 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/libsoup/soup-session.c 2025-07-12 14:43:24.000000000 +0100
@@ -1230,6 +1230,12 @@
SOUP_ENCODING_NONE);
}
+ /* Strip all credentials on cross-origin redirect. */
+ if (!soup_uri_host_equal (soup_message_get_uri (msg), new_uri)) {
+ soup_message_headers_remove_common (soup_message_get_request_headers (msg), SOUP_HEADER_AUTHORIZATION);
+ soup_message_set_auth (msg, NULL);
+ }
+
soup_message_set_request_host_from_uri (msg, new_uri);
soup_message_set_uri (msg, new_uri);
g_uri_unref (new_uri);
diff -Nru libsoup3-3.2.2/libsoup/soup-uri-utils.c libsoup3-3.2.3/libsoup/soup-uri-utils.c
--- libsoup3-3.2.2/libsoup/soup-uri-utils.c 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/libsoup/soup-uri-utils.c 2024-11-25 00:39:07.000000000 +0000
@@ -286,6 +286,7 @@
gboolean base64 = FALSE;
char *uri_string;
GBytes *bytes;
+ const char *path;
g_return_val_if_fail (uri != NULL, NULL);
@@ -301,8 +302,17 @@
if (content_type)
*content_type = NULL;
+ /* g_uri_to_string() is picky about paths that start with `//` and will assert. */
+ path = g_uri_get_path (soup_uri);
+ if (path[0] == '/' && path[1] == '/') {
+ g_uri_unref (soup_uri);
+ return NULL;
+ }
+
uri_string = g_uri_to_string (soup_uri);
g_uri_unref (soup_uri);
+ if (!uri_string)
+ return NULL;
start = uri_string + 5;
comma = strchr (start, ',');
diff -Nru libsoup3-3.2.2/libsoup/websocket/soup-websocket-connection.c libsoup3-3.2.3/libsoup/websocket/soup-websocket-connection.c
--- libsoup3-3.2.2/libsoup/websocket/soup-websocket-connection.c 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/libsoup/websocket/soup-websocket-connection.c 2024-11-25 00:39:07.000000000 +0000
@@ -1150,9 +1150,9 @@
}
priv->incoming->len = len + count;
- } while (count > 0);
- process_incoming (self);
+ process_incoming (self);
+ } while (count > 0 && !priv->close_sent && !priv->io_closing);
if (end) {
if (!priv->close_sent || !priv->close_received) {
diff -Nru libsoup3-3.2.2/meson.build libsoup3-3.2.3/meson.build
--- libsoup3-3.2.2/meson.build 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/meson.build 2024-11-25 00:39:07.000000000 +0000
@@ -1,5 +1,5 @@
project('libsoup', 'c',
- version: '3.2.2',
+ version: '3.2.3',
meson_version : '>= 0.54',
license : 'LGPL-2.0-or-later',
default_options : [
diff -Nru libsoup3-3.2.2/NEWS libsoup3-3.2.3/NEWS
--- libsoup3-3.2.2/NEWS 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/NEWS 2024-11-25 00:39:07.000000000 +0000
@@ -1,3 +1,11 @@
+Changes in libsoup from 3.2.2 to 3.2.3:
+
+* Fix possible NULL deref in `soup_uri_decode_data_uri()` [Ar Jun]
+* Fix possible overflow in `SoupContentSniffer` [Ar Jun]
+* Fix assertion in `soup_uri_decode_data_uri()` on URLs with a path starting with `//` [Patrick Griffis]
+* websocket: Fix possibility of being stuck in a read loop [Ignacio Casal Quinteiro]
+
+
Changes in libsoup from 3.2.1 to 3.2.2:
* Various HTTP/2 Fixes: [Carlos Garcia Campos]
diff -Nru libsoup3-3.2.2/tests/auth-test.c libsoup3-3.2.3/tests/auth-test.c
--- libsoup3-3.2.2/tests/auth-test.c 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/tests/auth-test.c 2025-07-12 14:43:24.000000000 +0100
@@ -1,6 +1,7 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
#include "test-utils.h"
+#include "soup-uri-utils-private.h"
static const char *base_uri;
static GMainLoop *loop;
@@ -1866,6 +1867,131 @@
soup_test_server_quit_unref (server);
}
+static void
+on_request_read_for_missing_params (SoupServer *server,
+ SoupServerMessage *msg,
+ gpointer user_data)
+{
+ const char *auth_header = user_data;
+ SoupMessageHeaders *response_headers = soup_server_message_get_response_headers (msg);
+ soup_message_headers_replace (response_headers, "WWW-Authenticate", auth_header);
+}
+
+static void
+do_missing_params_test (gconstpointer auth_header)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ SoupServer *server;
+ SoupAuthDomain *digest_auth_domain;
+ gint status;
+ GUri *uri;
+
+ server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
+ soup_server_add_handler (server, NULL,
+ server_callback, NULL, NULL);
+ uri = soup_test_server_get_uri (server, "http", NULL);
+
+ digest_auth_domain = soup_auth_domain_digest_new (
+ "realm", "auth-test",
+ "auth-callback", server_digest_auth_callback,
+ NULL);
+ soup_auth_domain_add_path (digest_auth_domain, "/");
+ soup_server_add_auth_domain (server, digest_auth_domain);
+ g_object_unref (digest_auth_domain);
+
+ g_signal_connect (server, "request-read",
+ G_CALLBACK (on_request_read_for_missing_params),
+ (gpointer)auth_header);
+
+ session = soup_test_session_new (NULL);
+ msg = soup_message_new_from_uri ("GET", uri);
+ g_signal_connect (msg, "authenticate",
+ G_CALLBACK (on_digest_authenticate),
+ NULL);
+
+ status = soup_test_session_send_message (session, msg);
+
+ g_assert_cmpint (status, ==, SOUP_STATUS_UNAUTHORIZED);
+ g_uri_unref (uri);
+ soup_test_server_quit_unref (server);
+}
+
+static void
+redirect_server_callback (SoupServer *server,
+ SoupServerMessage *msg,
+ const char *path,
+ GHashTable *query,
+ gpointer user_data)
+{
+ static gboolean redirected = FALSE;
+
+ if (!redirected) {
+ char *redirect_uri = g_uri_to_string (user_data);
+ soup_server_message_set_redirect (msg, SOUP_STATUS_MOVED_PERMANENTLY, redirect_uri);
+ g_free (redirect_uri);
+ redirected = TRUE;
+ return;
+ }
+
+ g_assert_not_reached ();
+}
+
+static gboolean
+auth_for_redirect_callback (SoupMessage *msg, SoupAuth *auth, gboolean retrying, gpointer user_data)
+{
+ GUri *known_server_uri = user_data;
+
+ if (!soup_uri_host_equal (known_server_uri, soup_message_get_uri (msg)))
+ return FALSE;
+
+ soup_auth_authenticate (auth, "user", "good-basic");
+
+ return TRUE;
+}
+
+static void
+do_strip_on_crossorigin_redirect (void)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ SoupServer *server1, *server2;
+ SoupAuthDomain *auth_domain;
+ GUri *uri;
+ gint status;
+
+ server1 = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
+ server2 = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
+
+ /* Both servers have the same credentials. */
+ auth_domain = soup_auth_domain_basic_new ("realm", "auth-test", "auth-callback", server_basic_auth_callback, NULL);
+ soup_auth_domain_add_path (auth_domain, "/");
+ soup_server_add_auth_domain (server1, auth_domain);
+ soup_server_add_auth_domain (server2, auth_domain);
+ g_object_unref (auth_domain);
+
+ /* Server 1 asks for auth, then redirects to Server 2. */
+ soup_server_add_handler (server1, NULL,
+ redirect_server_callback,
+ soup_test_server_get_uri (server2, "http", NULL), (GDestroyNotify)g_uri_unref);
+ /* Server 2 requires auth. */
+ soup_server_add_handler (server2, NULL, server_callback, NULL, NULL);
+
+ session = soup_test_session_new (NULL);
+ uri = soup_test_server_get_uri (server1, "http", NULL);
+ msg = soup_message_new_from_uri ("GET", uri);
+ /* The client only sends credentials for the host it knows. */
+ g_signal_connect (msg, "authenticate", G_CALLBACK (auth_for_redirect_callback), uri);
+
+ status = soup_test_session_send_message (session, msg);
+
+ g_assert_cmpint (status, ==, SOUP_STATUS_UNAUTHORIZED);
+
+ g_uri_unref (uri);
+ soup_test_server_quit_unref (server1);
+ soup_test_server_quit_unref (server2);
+}
+
int
main (int argc, char **argv)
{
@@ -1899,6 +2025,11 @@
g_test_add_func ("/auth/auth-uri", do_auth_uri_test);
g_test_add_func ("/auth/cancel-request-on-authenticate", do_cancel_request_on_authenticate);
g_test_add_func ("/auth/multiple-algorithms", do_multiple_digest_algorithms);
+ g_test_add_func ("/auth/strip-on-crossorigin-redirect", do_strip_on_crossorigin_redirect);
+ g_test_add_data_func ("/auth/missing-params/realm", "Digest qop=\"auth\"", do_missing_params_test);
+ g_test_add_data_func ("/auth/missing-params/nonce", "Digest realm=\"auth-test\", qop=\"auth,auth-int\", opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"", do_missing_params_test);
+ g_test_add_data_func ("/auth/missing-params/nonce-md5-sess", "Digest realm=\"auth-test\", qop=\"auth,auth-int\", opaque=\"5ccc069c403ebaf9f0171e9517f40e41\" algorithm=\"MD5-sess\"", do_missing_params_test);
+ g_test_add_data_func ("/auth/missing-params/nonce-and-qop", "Digest realm=\"auth-test\"", do_missing_params_test);
ret = g_test_run ();
diff -Nru libsoup3-3.2.2/tests/header-parsing-test.c libsoup3-3.2.3/tests/header-parsing-test.c
--- libsoup3-3.2.2/tests/header-parsing-test.c 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/tests/header-parsing-test.c 2025-07-12 14:43:24.000000000 +0100
@@ -6,6 +6,15 @@
const char *name, *value;
} Header;
+/* These are not C strings to ensure going one byte over is not safe. */
+static char unterminated_http_version[] = {
+ 'G','E','T',' ','/',' ','H','T','T','P','/','1', '0', '0', '.'
+};
+
+static char only_newlines[] = {
+ '\n', '\n', '\n', '\n'
+};
+
static struct RequestTest {
const char *description;
const char *bugref;
@@ -358,24 +367,6 @@
}
},
- { "NUL in header name", "760832",
- "GET / HTTP/1.1\r\nHost\x00: example.com\r\n", 36,
- SOUP_STATUS_OK,
- "GET", "/", SOUP_HTTP_1_1,
- { { "Host", "example.com" },
- { NULL }
- }
- },
-
- { "NUL in header value", "760832",
- "GET / HTTP/1.1\r\nHost: example\x00" "com\r\n", 35,
- SOUP_STATUS_OK,
- "GET", "/", SOUP_HTTP_1_1,
- { { "Host", "examplecom" },
- { NULL }
- }
- },
-
/************************/
/*** INVALID REQUESTS ***/
/************************/
@@ -401,6 +392,13 @@
{ { NULL } }
},
+ { "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 } }
+ },
+
{ "Non-HTTP request", NULL,
"GET / SOUP/1.1\r\nHost: example.com\r\n", -1,
SOUP_STATUS_BAD_REQUEST,
@@ -448,6 +446,28 @@
SOUP_STATUS_EXPECTATION_FAILED,
NULL, NULL, -1,
{ { NULL } }
+ },
+
+ // https://gitlab.gnome.org/GNOME/libsoup/-/issues/377
+ { "NUL in header name", NULL,
+ "GET / HTTP/1.1\r\nHost\x00: example.com\r\n", 36,
+ SOUP_STATUS_BAD_REQUEST,
+ NULL, NULL, -1,
+ { { NULL } }
+ },
+
+ { "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 } }
+ },
+
+ { "Only newlines", NULL,
+ only_newlines, sizeof (only_newlines),
+ SOUP_STATUS_BAD_REQUEST,
+ NULL, NULL, -1,
+ { { NULL } }
}
};
static const int num_reqtests = G_N_ELEMENTS (reqtests);
@@ -620,22 +640,6 @@
{ NULL } }
},
- { "NUL in header name", "760832",
- "HTTP/1.1 200 OK\r\nF\x00oo: bar\r\n", 28,
- SOUP_HTTP_1_1, SOUP_STATUS_OK, "OK",
- { { "Foo", "bar" },
- { NULL }
- }
- },
-
- { "NUL in header value", "760832",
- "HTTP/1.1 200 OK\r\nFoo: b\x00" "ar\r\n", 28,
- SOUP_HTTP_1_1, SOUP_STATUS_OK, "OK",
- { { "Foo", "bar" },
- { NULL }
- }
- },
-
/********************************/
/*** VALID CONTINUE RESPONSES ***/
/********************************/
@@ -768,6 +772,19 @@
{ { NULL }
}
},
+
+ // https://gitlab.gnome.org/GNOME/libsoup/-/issues/377
+ { "NUL in header name", NULL,
+ "HTTP/1.1 200 OK\r\nF\x00oo: bar\r\n", 28,
+ -1, 0, NULL,
+ { { NULL } }
+ },
+
+ { "NUL in header value", "760832",
+ "HTTP/1.1 200 OK\r\nFoo: b\x00" "ar\r\n", 28,
+ -1, 0, NULL,
+ { { NULL } }
+ },
};
static const int num_resptests = G_N_ELEMENTS (resptests);
@@ -831,6 +848,17 @@
{ "filename", "t\xC3\xA9st.txt" },
},
},
+
+ /* This tests invalid UTF-8 data which *should* never be passed here but it was designed to be robust against it. */
+ { TRUE,
+ "invalid*=\x69\x27\x27\x93\x93\x93\x93\xff\x61\x61\x61\x61\x61\x61\x61\x62\x63\x64\x65\x0a; filename*=iso-8859-1''\x69\x27\x27\x93\x93\x93\x93\xff\x61\x61\x61\x61\x61\x61\x61\x62\x63\x64\x65\x0a; foo",
+ {
+ { "filename", "i''\302\223\302\223\302\223\302\223\303\277aaaaaaabcde" },
+ { "invalid", "\302\223\302\223\302\223\302\223\303\277aaaaaaabcde" },
+ { "foo", NULL },
+
+ },
+ }
};
static const int num_paramlisttests = G_N_ELEMENTS (paramlisttests);
@@ -1034,6 +1062,7 @@
#define RFC5987_TEST_HEADER_FALLBACK "attachment; filename*=Unknown''t%FF%FF%FFst.txt; filename=\"test.txt\""
#define RFC5987_TEST_HEADER_NO_TYPE "filename=\"test.txt\""
#define RFC5987_TEST_HEADER_NO_TYPE_2 "filename=\"test.txt\"; foo=bar"
+#define RFC5987_TEST_HEADER_EMPTY_FILENAME ";filename"
static void
do_content_disposition_tests (void)
@@ -1134,6 +1163,20 @@
g_assert_cmpstr (parameter2, ==, "bar");
g_hash_table_destroy (params);
+ /* Empty filename */
+ soup_message_headers_clear (hdrs);
+ soup_message_headers_append (hdrs, "Content-Disposition",
+ RFC5987_TEST_HEADER_EMPTY_FILENAME);
+ if (!soup_message_headers_get_content_disposition (hdrs,
+ &disposition,
+ ¶ms)) {
+ soup_test_assert (FALSE, "empty filename decoding FAILED");
+ return;
+ }
+ g_free (disposition);
+ g_assert_false (g_hash_table_contains (params, "filename"));
+ g_hash_table_destroy (params);
+
soup_message_headers_unref (hdrs);
/* Ensure that soup-multipart always quotes filename */
diff -Nru libsoup3-3.2.2/tests/meson.build libsoup3-3.2.3/tests/meson.build
--- libsoup3-3.2.2/tests/meson.build 2025-07-12 14:43:24.000000000 +0100
+++ libsoup3-3.2.3/tests/meson.build 2025-07-12 14:43:24.000000000 +0100
@@ -95,7 +95,9 @@
{'name': 'session'},
{'name': 'server-auth'},
{'name': 'server'},
- {'name': 'sniffing'},
+ {'name': 'sniffing',
+ 'depends': [test_resources],
+ },
{'name': 'ssl',
'dependencies': [gnutls_dep],
'depends': mock_pkcs11_module,
diff -Nru libsoup3-3.2.2/tests/sniffing-test.c libsoup3-3.2.3/tests/sniffing-test.c
--- libsoup3-3.2.2/tests/sniffing-test.c 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/tests/sniffing-test.c 2025-07-12 14:43:24.000000000 +0100
@@ -342,6 +342,52 @@
g_uri_unref (uri);
}
+static const gsize MARKUP_LENGTH = strlen ("<!--") + strlen ("-->");
+
+static void
+do_skip_whitespace_test (void)
+{
+ SoupContentSniffer *sniffer = soup_content_sniffer_new ();
+ SoupMessage *msg = soup_message_new (SOUP_METHOD_GET, "http://example.org");
+ const char *test_cases[] = {
+ "",
+ "<rdf:RDF",
+ "<rdf:RDFxmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"",
+ "<rdf:RDFxmlns=\"http://purl.org/rss/1.0/\"",
+ };
+
+ soup_message_headers_set_content_type (soup_message_get_response_headers (msg), "text/html", NULL);
+
+ for (guint i = 0; i < G_N_ELEMENTS (test_cases); i++) {
+ const char *trailing_data = test_cases[i];
+ gsize leading_zeros = 512 - MARKUP_LENGTH - strlen (trailing_data);
+ gsize testsize = MARKUP_LENGTH + leading_zeros + strlen (trailing_data);
+ guint8 *data = g_malloc0 (testsize);
+ guint8 *p = data;
+ char *content_type;
+ GBytes *buffer;
+
+ // Format of <!--[0x00 * $leading_zeros]-->$trailing_data
+ memcpy (p, "<!--", strlen ("<!--"));
+ p += strlen ("<!--");
+ p += leading_zeros;
+ memcpy (p, "-->", strlen ("-->"));
+ p += strlen ("-->");
+ if (strlen (trailing_data))
+ memcpy (p, trailing_data, strlen (trailing_data));
+ // Purposefully not NUL terminated.
+
+ buffer = g_bytes_new_take (g_steal_pointer (&data), testsize);
+ content_type = soup_content_sniffer_sniff (sniffer, msg, buffer, NULL);
+
+ g_free (content_type);
+ g_bytes_unref (buffer);
+ }
+
+ g_object_unref (msg);
+ g_object_unref (sniffer);
+}
+
int
main (int argc, char **argv)
{
@@ -517,6 +563,8 @@
"/text_or_binary/home.gif",
test_disabled);
+ g_test_add_func ("/sniffing/whitespace", do_skip_whitespace_test);
+
ret = g_test_run ();
g_uri_unref (base_uri);
diff -Nru libsoup3-3.2.2/tests/uri-parsing-test.c libsoup3-3.2.3/tests/uri-parsing-test.c
--- libsoup3-3.2.2/tests/uri-parsing-test.c 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/tests/uri-parsing-test.c 2024-11-25 00:39:07.000000000 +0000
@@ -131,6 +131,8 @@
{ "data:text/plain;base64,aGVsbG8=", "hello", "text/plain" },
{ "data:text/plain;base64,invalid=", "", "text/plain" },
{ "data:,", "", CONTENT_TYPE_DEFAULT },
+ { "data:.///", NULL, NULL },
+ { "data:/.//", NULL, NULL },
};
static void
diff -Nru libsoup3-3.2.2/tests/websocket-test.c libsoup3-3.2.3/tests/websocket-test.c
--- libsoup3-3.2.2/tests/websocket-test.c 2022-11-02 19:46:22.000000000 +0000
+++ libsoup3-3.2.3/tests/websocket-test.c 2024-11-25 00:39:07.000000000 +0000
@@ -1408,8 +1408,9 @@
GError *error = NULL;
InvalidEncodeLengthTest context = { test, NULL };
guint i;
+ guint error_id;
- g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error);
+ error_id = g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error);
g_signal_connect (test->client, "message", G_CALLBACK (on_binary_message), &received);
/* We use 126(~) as payload length with 125 extended length */
@@ -1422,6 +1423,7 @@
WAIT_UNTIL (error != NULL || received != NULL);
g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR);
g_clear_error (&error);
+ g_signal_handler_disconnect (test->client, error_id);
g_assert_null (received);
g_thread_join (thread);
@@ -1439,8 +1441,9 @@
GError *error = NULL;
InvalidEncodeLengthTest context = { test, NULL };
guint i;
+ guint error_id;
- g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error);
+ error_id = g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error);
g_signal_connect (test->client, "message", G_CALLBACK (on_binary_message), &received);
/* We use 127(\x7f) as payload length with 65535 extended length */
@@ -1453,6 +1456,7 @@
WAIT_UNTIL (error != NULL || received != NULL);
g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR);
g_clear_error (&error);
+ g_signal_handler_disconnect (test->client, error_id);
g_assert_null (received);
g_thread_join (thread);
Reply to: