Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package xdg-dbus-proxy for openSUSE:Factory 
checked in at 2026-09-28 10:35:18
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/xdg-dbus-proxy (Old)
 and      /work/SRC/openSUSE:Factory/.xdg-dbus-proxy.new.383539 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "xdg-dbus-proxy"

Mon Sep 28 10:35:18 2026 rev:10 rq:1380187 version:0.1.9

Changes:
--------
--- /work/SRC/openSUSE:Factory/xdg-dbus-proxy/xdg-dbus-proxy.changes    
2026-08-22 21:34:54.754564082 +0200
+++ 
/work/SRC/openSUSE:Factory/.xdg-dbus-proxy.new.383539/xdg-dbus-proxy.changes    
    2026-09-28 10:35:59.051643721 +0200
@@ -1,0 +2,13 @@
+Wed Sep 23 19:28:11 UTC 2026 - Dominique Leuenberger <[email protected]>
+
+- Update to version 0.1.9:
+  + Fix message filtering bypass vulnerabilities (CVE-2026-94422,
+    GHSA-2cgv-pwcq-wvpq):
+    - Don't allow method calls and signals to be treated as
+      requested replies, even if they specify a reply serial number
+    - Only allow replies that were sent to the appropriate
+      destination
+  + Improve automated tests to include attempts to exploit
+    CVE-2026-94422
+
+-------------------------------------------------------------------
@@ -5,0 +19 @@
+    (CVE-2026-93676, GHSA-r7hp-698j-2h6c)
@@ -24 +38 @@
-  + Fix CVE-2026-34080
+    (CVE-2026-34080, GHSA-vjp5-hjfm-7677)

Old:
----
  xdg-dbus-proxy-0.1.8.tar.xz

New:
----
  xdg-dbus-proxy-0.1.9.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ xdg-dbus-proxy.spec ++++++
--- /var/tmp/diff_new_pack.7MTrGn/_old  2026-09-28 10:35:59.967682118 +0200
+++ /var/tmp/diff_new_pack.7MTrGn/_new  2026-09-28 10:35:59.968682160 +0200
@@ -18,7 +18,7 @@
 
 
 Name:           xdg-dbus-proxy
-Version:        0.1.8
+Version:        0.1.9
 Release:        0
 Summary:        Filtering proxy for D-Bus connections
 License:        LGPL-2.1-or-later

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.7MTrGn/_old  2026-09-28 10:36:00.001683543 +0200
+++ /var/tmp/diff_new_pack.7MTrGn/_new  2026-09-28 10:36:00.006683753 +0200
@@ -1,6 +1,7 @@
-mtime: 1787214355
-commit: 3ce2c8449f3e5125299f57cce14b06e0b1ff1ab847c7748a3a3c082758f1e11d
+mtime: 1790249885
+commit: a81bb3de071015979779f2ac1e28471db7ad76fb72287c3e849990b28c4058b8
 url: https://src.opensuse.org/GNOME/xdg-dbus-proxy
-revision: 3ce2c8449f3e5125299f57cce14b06e0b1ff1ab847c7748a3a3c082758f1e11d
+revision: a81bb3de071015979779f2ac1e28471db7ad76fb72287c3e849990b28c4058b8
+trackingbranch: factory
 projectscmsync: https://src.opensuse.org/GNOME/_ObsPrj
 

++++++ build.specials.obscpio ++++++
--- old/.gitignore      2026-08-20 10:25:55.000000000 +0200
+++ new/.gitignore      2026-09-24 13:38:05.000000000 +0200
@@ -2,3 +2,4 @@
 *.osc
 _build.*
 .pbuild
+osc-collab.*

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-09-24 13:38:05.000000000 +0200
@@ -0,0 +1,5 @@
+*.obscpio
+*.osc
+_build.*
+.pbuild
+osc-collab.*

++++++ xdg-dbus-proxy-0.1.8.tar.xz -> xdg-dbus-proxy-0.1.9.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xdg-dbus-proxy-0.1.8/NEWS 
new/xdg-dbus-proxy-0.1.9/NEWS
--- old/xdg-dbus-proxy-0.1.8/NEWS       2026-08-11 14:05:09.000000000 +0200
+++ new/xdg-dbus-proxy-0.1.9/NEWS       2026-09-23 15:42:04.000000000 +0200
@@ -1,9 +1,22 @@
+Changes in 0.1.9
+================
+
+Released: 2026-09-23
+
+  * Fix message filtering bypass vulnerabilities
+    (CVE-2026-94422, GHSA-2cgv-pwcq-wvpq)
+      - Don't allow method calls and signals to be treated as requested
+        replies, even if they specify a reply serial number
+      - Only allow replies that were sent to the appropriate destination
+  * Improve automated tests to include attempts to exploit CVE-2026-94422
+
 Changes in 0.1.8
 ================
 
 Released 2026-08-11
 
   * Fix broadcast messages bypassing path/interface/member checks
+    (CVE-2026-93676, GHSA-r7hp-698j-2h6c)
   * Improvements to the existing testing infrastructure
   * Add tests for owning names, issuing method calls, receiving messages
 
@@ -17,6 +30,7 @@
   * Prevent a crash on disconnect
   * Fix building with glibc >= 2.43
   * Fix the eavesdrop filtering to prevent message interception
+    (CVE-2026-34080, GHSA-vjp5-hjfm-7677)
 
 Changes in 0.1.6
 ================
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xdg-dbus-proxy-0.1.8/flatpak-proxy.c 
new/xdg-dbus-proxy-0.1.9/flatpak-proxy.c
--- old/xdg-dbus-proxy-0.1.8/flatpak-proxy.c    2026-08-11 14:05:09.000000000 
+0200
+++ new/xdg-dbus-proxy-0.1.9/flatpak-proxy.c    2026-09-23 15:42:04.000000000 
+0200
@@ -123,9 +123,7 @@
  * Once authenticated we receive incoming messages one at a time,
  * and then we demarshal the message headers to make routing decisions.
  * This means we trust the bus to do message format validation, etc.
- * (because we don't parse the body). Also we assume that the bus verifies
- * reply_serials, i.e. that a reply can only be sent once and by the real
- * recipient of an previously sent method call.
+ * (because we don't parse the body).
  *
  * Serial numbers larger than MAX_CLIENT_SERIAL reserved for messages created 
by the
  * proxy itself (fake messages). This limits the possible values of serials
@@ -252,7 +250,6 @@
   const char *destination;
   const char *sender;
   const char *signature;
-  gboolean    has_reply_serial;
   guint32     reply_serial;
   guint32     unix_fds;
 } Header;
@@ -281,6 +278,61 @@
 static void header_free (Header *header);
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (Header, header_free)
 
+/*
+ * A pending method call from @caller on the bus to the client,
+ * for which we expect a reply from the client back to @caller,
+ * represented as a single memory block.
+ */
+typedef struct
+{
+  guint32 serial;
+  char caller[];
+} ExpectedReplyFromClient;
+
+static ExpectedReplyFromClient *
+expected_reply_from_client_new (guint32 serial,
+                                const char *caller)
+{
+  ExpectedReplyFromClient *ret;
+  size_t len;
+
+  /* A missing destination on the method call means a method call from
+   * the message bus itself (unlikely, but possible).
+   * We represent this as the empty string (which is not a valid bus name)
+   * to avoid needing a separate representation for NULL. */
+  if (caller == NULL)
+    caller = "";
+
+  len = strlen (caller) + 1;
+
+  ret = g_malloc0 (sizeof (*ret) + len);
+  ret->serial = serial;
+  memcpy (&ret->caller, caller, len);
+  return ret;
+}
+
+/* Because it's a single memory block, we can just g_free() it. */
+#define expected_reply_from_client_free g_free
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (ExpectedReplyFromClient, 
expected_reply_from_client_free)
+
+static gboolean
+expected_reply_from_client_equal (const void *v1,
+                                  const void *v2)
+{
+  const ExpectedReplyFromClient *left = v1;
+  const ExpectedReplyFromClient *right = v2;
+
+  return left->serial == right->serial && g_str_equal (&left->caller, 
&right->caller);
+}
+
+static guint
+expected_reply_from_client_hash (const void *v)
+{
+  const ExpectedReplyFromClient *self = v;
+
+  return self->serial ^ g_str_hash (&self->caller);
+}
+
 typedef struct
 {
   gboolean            got_first_byte; /* always true on bus side */
@@ -297,8 +349,6 @@
 
   GList              *buffers; /* to be sent */
   GList              *control_messages;
-
-  GHashTable         *expected_replies;
 } ProxySide;
 
 struct FlatpakProxyClient
@@ -315,6 +365,31 @@
   ProxySide     client_side;
   ProxySide     bus_side;
 
+  /* (element-type ExpectedReplyFromClient ExpectedReplyType)
+   * Map from message serial number and caller to expected replies.
+   * If this map contains
+   * { { serial, caller }: EXPECTED_REPLY_FOO },
+   * then it means that @caller on the bus sent a method call to the
+   * sandboxed client with SERIAL=serial.
+   * This means that in future we're expecting the sandboxed client to
+   * respond with a message back to @caller with REPLY_SERIAL=serial. */
+  GHashTable   *expected_replies_from_client;
+
+  /* (element-type guint ExpectedReplyType)
+   * Map from message serial number to expected reply type.
+   * If this map contains
+   * { serial: EXPECTED_REPLY_FOO },
+   * then it means that the sandboxed client sent a method call to the
+   * bus with SERIAL=serial.
+   * This means that in future we're expecting something on the bus to
+   * respond with a message that has REPLY_SERIAL=serial.
+   * This direction is simpler than the other way round because we don't
+   * need to distinguish between destinations: we trust the bus,
+   * and there is only one possible destination on the sandboxed client
+   * side anyway.
+   */
+  GHashTable   *expected_replies_from_bus;
+
   /* Filtering data: */
   guint32     hello_serial;
   guint32     last_fake_serial;
@@ -416,8 +491,6 @@
     g_source_destroy (side->in_source);
   if (side->out_source)
     g_source_destroy (side->out_source);
-
-  g_hash_table_destroy (side->expected_replies);
 }
 
 static void
@@ -433,6 +506,8 @@
   g_hash_table_destroy (client->get_owner_reply);
   g_hash_table_destroy (client->unique_id_policy);
   g_hash_table_destroy (client->unique_id_owned_names);
+  g_hash_table_destroy (client->expected_replies_from_bus);
+  g_hash_table_destroy (client->expected_replies_from_client);
 
   free_side (&client->client_side);
   free_side (&client->bus_side);
@@ -456,7 +531,6 @@
   side->header_buffer.size = 16;
   side->header_buffer.pos = 0;
   side->current_read_buffer = &side->header_buffer;
-  side->expected_replies = g_hash_table_new (g_direct_hash, g_direct_equal);
 }
 
 static void
@@ -471,6 +545,11 @@
   client->get_owner_reply = g_hash_table_new_full (g_direct_hash, 
g_direct_equal, NULL, g_free);
   client->unique_id_policy = g_hash_table_new_full (g_str_hash, g_str_equal, 
g_free, NULL);
   client->unique_id_owned_names = g_hash_table_new_full (g_str_hash, 
g_str_equal, g_free, (GDestroyNotify) string_list_free);
+  client->expected_replies_from_client = g_hash_table_new_full 
(expected_reply_from_client_hash,
+                                                                
expected_reply_from_client_equal,
+                                                                
expected_reply_from_client_free,
+                                                                NULL);
+  client->expected_replies_from_bus = g_hash_table_new (g_direct_hash, 
g_direct_equal);
 }
 
 static FlatpakProxyClient *
@@ -1066,27 +1145,102 @@
     }
 }
 
+/*
+ * A method call was received from the sandboxed client,
+ * with serial number @serial.
+ * We now expect a reply from the bus side, of type @type.
+ * We do not need to track the expected destination,
+ * because there is only one destination on the client side
+ * (and we don't know its name until we have delivered the Hello reply).
+ */
 static void
-queue_expected_reply (ProxySide *side, guint32 serial, ExpectedReplyType type)
+queue_expected_reply_from_bus (FlatpakProxyClient *client,
+                               guint32 serial,
+                               ExpectedReplyType type)
 {
-  g_hash_table_replace (side->expected_replies,
+  g_return_if_fail (serial != 0);
+  g_return_if_fail (type != EXPECTED_REPLY_NONE);
+
+  g_hash_table_replace (client->expected_replies_from_bus,
                         GUINT_TO_POINTER (serial),
                         GUINT_TO_POINTER (type));
 }
 
+/*
+ * A method call was received from sender @caller on the bus,
+ * with serial number @serial.
+ * We now expect a reply from the sandboxed client of type @type.
+ */
+static void
+queue_expected_reply_from_client (FlatpakProxyClient *client,
+                                  guint32 serial,
+                                  const char *caller,
+                                  ExpectedReplyType type)
+{
+  g_return_if_fail (serial != 0);
+  g_return_if_fail (caller == NULL || caller[0] != '\0');
+  g_return_if_fail (type != EXPECTED_REPLY_NONE);
+
+  g_hash_table_replace (client->expected_replies_from_client,
+                        expected_reply_from_client_new (serial, caller),
+                        GUINT_TO_POINTER (type));
+}
+
+
+/*
+ * @client: The client
+ * @serial: The reply_serial field of the reply
+ *
+ * Returns: The type of the matching reply, or %EXPECTED_REPLY_NONE
+ *  if no match was found.
+ */
 static ExpectedReplyType
-steal_expected_reply (ProxySide *side, guint32 serial)
+steal_expected_reply_from_bus (FlatpakProxyClient *client,
+                               guint32 serial)
 {
   ExpectedReplyType type;
 
-  type = GPOINTER_TO_UINT (g_hash_table_lookup (side->expected_replies,
+  g_return_val_if_fail (serial != 0, EXPECTED_REPLY_NONE);
+
+  type = GPOINTER_TO_UINT (g_hash_table_lookup 
(client->expected_replies_from_bus,
                                                 GUINT_TO_POINTER (serial)));
   if (type)
-    g_hash_table_remove (side->expected_replies,
+    g_hash_table_remove (client->expected_replies_from_bus,
                          GUINT_TO_POINTER (serial));
   return type;
 }
 
+/*
+ * @client: The client
+ * @serial: The reply_serial field of the reply
+ * @caller: Only match pending method calls that came from @caller
+ *  (and therefore the reply should be sent to @caller).
+ *
+ * Returns: The type of the matching reply, or %EXPECTED_REPLY_NONE
+ *  if no match was found.
+ */
+static ExpectedReplyType
+steal_expected_reply_from_client (FlatpakProxyClient *client,
+                                  guint32 serial,
+                                  const char *caller)
+{
+  g_autoptr(ExpectedReplyFromClient) key = NULL;
+  void *type;
+
+  g_return_val_if_fail (serial != 0, EXPECTED_REPLY_NONE);
+  g_return_val_if_fail (caller == NULL || caller[0] != '\0', 
EXPECTED_REPLY_NONE);
+
+  key = expected_reply_from_client_new (serial, caller);
+
+  if (g_hash_table_lookup_extended (client->expected_replies_from_client, key,
+                                    NULL, &type))
+    {
+      g_hash_table_remove (client->expected_replies_from_client, key);
+      return GPOINTER_TO_UINT (type);
+    }
+
+  return EXPECTED_REPLY_NONE;
+}
 
 static void
 queue_outgoing_buffer (ProxySide *side, Buffer *buffer)
@@ -1221,6 +1375,22 @@
   return s->str;
 }
 
+static gboolean
+is_reply (Header *header)
+{
+  switch (header->type)
+    {
+    case G_DBUS_MESSAGE_TYPE_METHOD_RETURN:
+    case G_DBUS_MESSAGE_TYPE_ERROR:
+      return TRUE;
+
+    case G_DBUS_MESSAGE_TYPE_METHOD_CALL:
+    case G_DBUS_MESSAGE_TYPE_SIGNAL:
+    default:
+      return FALSE;
+    }
+}
+
 static Header *
 parse_header (Buffer *buffer, GError **error)
 {
@@ -1228,6 +1398,7 @@
   guint32 offset, end_offset;
   guint8 header_type;
   const char *signature;
+  gboolean has_reply_serial = FALSE;
   g_autoptr(GError) str_error = NULL;
   g_autoptr(GString) header_str = NULL;
 
@@ -1452,7 +1623,7 @@
               return NULL;
             }
 
-          header->has_reply_serial = TRUE;
+          has_reply_serial = TRUE;
           header->reply_serial = read_uint32 (header, &buffer->data[offset]);
           offset += 4;
           break;
@@ -1570,7 +1741,7 @@
       break;
 
     case G_DBUS_MESSAGE_TYPE_METHOD_RETURN:
-      if (!header->has_reply_serial)
+      if (!has_reply_serial)
         {
           g_set_error (error,
                        G_IO_ERROR,
@@ -1582,7 +1753,7 @@
       break;
 
     case G_DBUS_MESSAGE_TYPE_ERROR:
-      if (header->error_name  == NULL || !header->has_reply_serial)
+      if (header->error_name  == NULL || !has_reply_serial)
         {
           g_set_error (error,
                        G_IO_ERROR,
@@ -1628,6 +1799,12 @@
       return NULL;
     }
 
+  /* Invariant: every reply has a reply serial.
+   * (Note that the converse is not true: it is technically possible to
+   * send a method call or signal that claims to be a reply.) */
+  if (is_reply (header))
+    g_assert (has_reply_serial);
+
   return g_steal_pointer (&header);
 }
 
@@ -1972,6 +2149,9 @@
     g_strcmp0 (header->interface, "org.freedesktop.DBus.Introspectable") == 0;
 }
 
+/*
+ * @header: an outgoing message from the sandboxed client to the bus
+ */
 static BusHandler
 get_dbus_method_handler (FlatpakProxyClient *client, Header *header)
 {
@@ -1980,11 +2160,17 @@
 
   g_autoptr(GList) filters = NULL;
 
-  if (header->has_reply_serial)
+  /* If the message claims to be a reply to a method call with serial number n,
+   * we allow if and only if there is indeed a pending method call from
+   * the reply's destination, with serial number n, and it has not yet had
+   * its reply */
+  if (is_reply (header))
     {
       ExpectedReplyType expected_reply =
-        steal_expected_reply (&client->bus_side,
-                              header->reply_serial);
+        steal_expected_reply_from_client (client,
+                                          header->reply_serial,
+                                          header->destination);
+
       if (expected_reply == EXPECTED_REPLY_NONE)
         return HANDLE_DENY;
 
@@ -2348,7 +2534,7 @@
   g_object_unref (message);
 
   queue_outgoing_buffer (&client->bus_side, buffer);
-  queue_expected_reply (&client->client_side, client->last_fake_serial, 
reply_type);
+  queue_expected_reply_from_bus (client, client->last_fake_serial, reply_type);
 }
 
 /* After the first Hello message we need to synthesize a bunch of messages to 
synchronize the
@@ -2473,12 +2659,17 @@
     }
 }
 
-
+/*
+ * Called when we received @buffer from the client side of @client:
+ * it's an outgoing message from the sandboxed client to the bus.
+ */
 static void
 got_buffer_from_client (FlatpakProxyClient *client, ProxySide *side, Buffer 
*buffer)
 {
   ExpectedReplyType expecting_reply = EXPECTED_REPLY_NONE;
 
+  g_assert (side == &client->client_side);
+
   if (client->auth_state == AUTH_COMPLETE && client->proxy->filter)
     {
       g_autoptr(Header) header = NULL;
@@ -2492,18 +2683,18 @@
         {
           g_warning ("Invalid message header format from client: %s",
                      error->message);
-          side_closed (side);
+          side_closed (&client->client_side);
           buffer_unref (buffer);
           return;
         }
 
-      if (!update_socket_messages (side, buffer, header))
+      if (!update_socket_messages (&client->client_side, buffer, header))
         return;
 
       if (header->serial > MAX_CLIENT_SERIAL)
         {
           g_warning ("Invalid client serial: Exceeds maximum value of %u", 
MAX_CLIENT_SERIAL);
-          side_closed (side);
+          side_closed (&client->client_side);
           buffer_unref (buffer);
           return;
         }
@@ -2633,7 +2824,7 @@
         }
 
       if (buffer != NULL && expecting_reply != EXPECTED_REPLY_NONE)
-        queue_expected_reply (side, header->serial, expecting_reply);
+        queue_expected_reply_from_bus (client, header->serial, 
expecting_reply);
     }
 
   if (buffer)
@@ -2643,9 +2834,15 @@
     queue_initial_name_ops (client);
 }
 
+/*
+ * Called when we received @buffer from the bus side of @client:
+ * it's an incoming message from the bus to the sandboxed client.
+ */
 static void
 got_buffer_from_bus (FlatpakProxyClient *client, ProxySide *side, Buffer 
*buffer)
 {
+  g_assert (side == &client->bus_side);
+
   if (client->auth_state == AUTH_COMPLETE && client->proxy->filter)
     {
       g_autoptr(Header) header = NULL;
@@ -2662,19 +2859,26 @@
           g_warning ("Invalid message header format from bus: %s",
                      error->message);
           buffer_unref (buffer);
-          side_closed (side);
+          side_closed (&client->bus_side);
           return;
         }
 
-      if (!update_socket_messages (side, buffer, header))
+      if (!update_socket_messages (&client->bus_side, buffer, header))
         return;
 
       if (client->proxy->log_messages)
         print_incoming_header (header);
 
-      if (header->has_reply_serial)
+      if (is_reply (header))
         {
-          expected_reply = steal_expected_reply (get_other_side (side), 
header->reply_serial);
+          /* If the client previously made a method call out to the bus
+           * with serial number n, we allow one reply to come from
+           * the bus "in reply to: n" back to the client.
+           * We allow any destination - this is OK, because we trust
+           * the bus, and in any case there is only one valid destination
+           * on the client side (it's the sandboxed client). */
+          expected_reply = steal_expected_reply_from_bus (client,
+                                                          
header->reply_serial);
 
           switch (expected_reply)
             {
@@ -2781,15 +2985,8 @@
         }
       else /* Not reply */
         {
-
-          /* Don't allow reply types with no reply_serial */
-          if (header->type == G_DBUS_MESSAGE_TYPE_METHOD_RETURN ||
-              header->type == G_DBUS_MESSAGE_TYPE_ERROR)
-            {
-              if (client->proxy->log_messages)
-                g_print ("*Invalid reply*\n");
-              g_clear_pointer (&buffer, buffer_unref);
-            }
+          g_assert (header->type != G_DBUS_MESSAGE_TYPE_METHOD_RETURN);
+          g_assert (header->type != G_DBUS_MESSAGE_TYPE_ERROR);
 
           /* We filter all NameOwnerChanged signal according to the policy */
           if (message_is_name_owner_changed (client, header))
@@ -2828,8 +3025,10 @@
       if (buffer && header->sender && header->sender[0] == ':')
         flatpak_proxy_client_update_unique_id_policy (client, header->sender, 
FLATPAK_POLICY_SEE);
 
+      /* Remember that the sandboxed client is allowed to send a single
+       * reply back to header->sender, marked "in reply to" header->serial. */
       if (buffer && client_message_generates_reply (header))
-        queue_expected_reply (side, header->serial, EXPECTED_REPLY_NORMAL);
+        queue_expected_reply_from_client (client, header->serial, 
header->sender, EXPECTED_REPLY_NORMAL);
     }
 
   if (buffer)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xdg-dbus-proxy-0.1.8/meson.build 
new/xdg-dbus-proxy-0.1.9/meson.build
--- old/xdg-dbus-proxy-0.1.8/meson.build        2026-08-11 14:05:09.000000000 
+0200
+++ new/xdg-dbus-proxy-0.1.9/meson.build        2026-09-23 15:42:04.000000000 
+0200
@@ -1,7 +1,7 @@
 project(
   'xdg-dbus-proxy',
   'c',
-  version : '0.1.8',
+  version : '0.1.9',
   meson_version : '>=0.49.0',
   default_options : [
     'warning_level=2',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xdg-dbus-proxy-0.1.8/tests/test-proxy.c 
new/xdg-dbus-proxy-0.1.9/tests/test-proxy.c
--- old/xdg-dbus-proxy-0.1.8/tests/test-proxy.c 2026-08-11 14:05:09.000000000 
+0200
+++ new/xdg-dbus-proxy-0.1.9/tests/test-proxy.c 2026-09-23 15:42:04.000000000 
+0200
@@ -40,6 +40,7 @@
 #define DBUS_REQUEST_NAME_REPLY_EXISTS 3
 #define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4
 
+#define CALLER_NAME "com.example.Caller"
 #define CANNOT_ACCESS_NAME "com.example.CannotAccess"
 #define CAN_SEE_NAME "com.example.CanSee"
 #define CAN_TALK_NAME "com.example.CanTalk"
@@ -60,6 +61,13 @@
 #define CAN_RECEIVE_SOME_SIGNAL "JustThisSignal"
 #define CAN_RECEIVE_SOME_PATH "/just/this/path"
 
+#define IGNORE_IFACE "com.example.Ignore"
+#define IGNORE_METHOD "Ignore"
+
+#define MALICIOUS_IFACE "com.example.Malice"
+#define MALICIOUS_MEMBER "ShouldNotBeAllowed"
+#define MALICIOUS_BODY "Message containing this body should have been blocked"
+
 static void
 ready_cb (GObject *source_object,
           GAsyncResult *result,
@@ -109,6 +117,7 @@
 typedef struct
 {
   Connection proxied;
+  Connection caller_conn;
   Connection cannot_access_conn;
   Connection can_see_conn;
   Connection can_talk_conn;
@@ -167,6 +176,16 @@
           case G_DBUS_MESSAGE_TYPE_METHOD_CALL:
             g_test_message ("%s got method call", conn_info->label);
             g_atomic_int_inc (&conn_info->n_method_calls);
+
+            if (g_strcmp0 (iface, IGNORE_IFACE) == 0 &&
+                g_strcmp0 (member, IGNORE_METHOD) == 0)
+              {
+                g_test_message ("method call is %s.%s, ignoring",
+                                iface, member);
+                g_clear_object (&message);
+                return NULL;
+              }
+
             break;
 
           case G_DBUS_MESSAGE_TYPE_SIGNAL:
@@ -188,11 +207,22 @@
             break;
 
           case G_DBUS_MESSAGE_TYPE_METHOD_RETURN:
-            g_test_message ("%s got method reply", conn_info->label);
-            break;
-
           case G_DBUS_MESSAGE_TYPE_ERROR:
-            g_test_message ("%s got error reply", conn_info->label);
+            if (type == G_DBUS_MESSAGE_TYPE_METHOD_RETURN)
+              g_test_message ("%s got method reply", conn_info->label);
+            else
+              g_test_message ("%s got error reply", conn_info->label);
+
+            if (g_str_equal (g_dbus_message_get_signature (message), "s"))
+              {
+                const char *s = NULL;
+
+                g_variant_get (g_dbus_message_get_body (message), "(&s)", &s);
+
+                if (g_str_equal (s, MALICIOUS_BODY))
+                  g_error ("Forged reply received");
+              }
+
             break;
 
           case G_DBUS_MESSAGE_TYPE_INVALID:
@@ -269,6 +299,51 @@
 }
 
 static void
+do_round_trip (const Connection *caller,
+               const Connection *destination)
+{
+  g_autoptr(GAsyncResult) result = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GVariant) tuple = NULL;
+
+  g_dbus_connection_call (caller->conn,
+                          destination->unique_name,
+                          "/",
+                          EXAMPLE_IFACE,
+                          EXAMPLE_METHOD,
+                          NULL,
+                          G_VARIANT_TYPE ("()"),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          -1,     /* timeout */
+                          NULL,   /* cancellable */
+                          ready_cb,
+                          &result);
+
+  while (result == NULL)
+    g_main_context_iteration (NULL, TRUE);
+
+  tuple = g_dbus_connection_call_finish (caller->conn, result, &error);
+
+  /* For simplicity we didn't actually implement any method calls,
+   * so the result should be an error. */
+  g_assert_nonnull (error);
+  g_assert_null (tuple);
+  g_assert_cmpstr (g_quark_to_string (error->domain), ==, g_quark_to_string 
(G_DBUS_ERROR));
+
+  switch (error->code)
+    {
+      case G_DBUS_ERROR_UNKNOWN_METHOD:
+      case G_DBUS_ERROR_UNKNOWN_INTERFACE:
+      case G_DBUS_ERROR_UNKNOWN_OBJECT:
+        /* OK */
+        break;
+
+      default:
+        g_assert_not_reached ();
+    }
+}
+
+static void
 setup (Fixture *f,
        gconstpointer context G_GNUC_UNUSED)
 {
@@ -346,6 +421,7 @@
                                                   g_free,
                                                   NULL);
 
+  fixture_connect (f, &f->caller_conn, CALLER_NAME);
   fixture_connect (f, &f->cannot_access_conn, CANNOT_ACCESS_NAME);
   fixture_connect (f, &f->can_see_conn, CAN_SEE_NAME);
   fixture_connect (f, &f->can_talk_conn, CAN_TALK_NAME);
@@ -824,6 +900,200 @@
     }
 }
 
+typedef struct
+{
+  const char *label;
+  GDBusMessageType type;
+  unsigned broadcast : 1;
+  unsigned misdirected : 1;
+} ReplyTest;
+
+static const ReplyTest reply_tests[] =
+{
+    { "should not be able to send a unicast signal in reply",
+      G_DBUS_MESSAGE_TYPE_SIGNAL },
+    { "should not be able to send a broadcast signal in reply",
+      G_DBUS_MESSAGE_TYPE_SIGNAL, .broadcast = TRUE },
+    { "should not be able to send a unicast signal to someone else in reply",
+      G_DBUS_MESSAGE_TYPE_SIGNAL, .misdirected = TRUE },
+    { "should not be able to send a method call in reply",
+      G_DBUS_MESSAGE_TYPE_METHOD_CALL },
+    { "should not be able to send a method call to someone else in reply",
+      G_DBUS_MESSAGE_TYPE_METHOD_CALL, .misdirected = TRUE },
+    { "should be able to send a method return in reply",
+      G_DBUS_MESSAGE_TYPE_METHOD_RETURN },
+    { "should not be able to send a method return to someone else in reply",
+      G_DBUS_MESSAGE_TYPE_METHOD_RETURN, .misdirected = TRUE },
+    { "should be able to send an error in reply",
+      G_DBUS_MESSAGE_TYPE_ERROR },
+    { "should not be able to send an error to someone else in reply",
+      G_DBUS_MESSAGE_TYPE_ERROR, .misdirected = TRUE },
+};
+
+static void
+test_reply (Fixture *f,
+            gconstpointer context G_GNUC_UNUSED)
+{
+  alarm (30);
+  fixture_start_proxy (f);
+
+  /* If a process outside the sandbox calls a method on the
+   * sandboxed process, then the sandboxed process is allowed to reply,
+   * but is not allowed to fake a "reply" that is really a method call
+   * or signal. (GHSA-2cgv-pwcq-wvpq) */
+  for (size_t i = 0; i < G_N_ELEMENTS (reply_tests); i++)
+    {
+      const ReplyTest *t = &reply_tests[i];
+      g_autoptr(GDBusMessage) call = NULL;
+      g_autoptr(GDBusMessage) reply = NULL;
+      g_autoptr(GError) error = NULL;
+      guint32 call_serial = 0;
+      int caller_calls_before;
+      int caller_broadcast_before;
+      int caller_unicast_before;
+      int other_calls_before;
+      int other_broadcast_before;
+      int other_unicast_before;
+
+      g_test_message ("#%zu: %s", i, t->label);
+
+      caller_calls_before = g_atomic_int_get (&f->caller_conn.n_method_calls);
+      caller_unicast_before = g_atomic_int_get 
(&f->caller_conn.n_unicast_signals);
+      caller_broadcast_before = g_atomic_int_get 
(&f->caller_conn.n_broadcasts);
+
+      other_calls_before = g_atomic_int_get 
(&f->cannot_access_conn.n_method_calls);
+      other_unicast_before = g_atomic_int_get 
(&f->cannot_access_conn.n_unicast_signals);
+      other_broadcast_before = g_atomic_int_get 
(&f->cannot_access_conn.n_broadcasts);
+
+      call = g_dbus_message_new_method_call (f->proxied.unique_name,
+                                             "/",
+                                             IGNORE_IFACE,
+                                             IGNORE_METHOD);
+      g_dbus_connection_send_message (f->caller_conn.conn,
+                                      call,
+                                      G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+                                      &call_serial,
+                                      &error);
+      g_assert_no_error (error);
+      g_assert_cmpuint (call_serial, !=, 0);
+      g_test_message ("Method call was serial number %u", call_serial);
+
+      /* Do a round-trip from the caller to the sandboxed connection and back.
+       * D-Bus messages are delivered sequentially, so by the time this call
+       * has finished, the method call will also have passed through the
+       * xdg-dbus-proxy and the dbus-daemon. */
+      do_round_trip (&f->caller_conn, &f->proxied);
+
+      switch (t->type)
+        {
+          case G_DBUS_MESSAGE_TYPE_SIGNAL:
+            reply = g_dbus_message_new_signal ("/",
+                                               MALICIOUS_IFACE,
+                                               MALICIOUS_MEMBER);
+
+            if (t->broadcast)
+              {
+                g_test_message ("Sending malicious broadcast signal as a 
reply");
+              }
+            else if (t->misdirected)
+              {
+                g_test_message ("Sending malicious unicast signal reply to 
wrong destination");
+                g_dbus_message_set_destination (reply, 
f->cannot_access_conn.unique_name);
+              }
+            else
+              {
+                g_test_message ("Sending malicious unicast signal as a reply");
+                g_dbus_message_set_destination (reply, 
f->caller_conn.unique_name);
+              }
+
+            break;
+
+          case G_DBUS_MESSAGE_TYPE_METHOD_CALL:
+            if (t->misdirected)
+              {
+                g_test_message ("Sending malicious method call to wrong 
destination");
+                reply = g_dbus_message_new_method_call 
(f->cannot_access_conn.unique_name,
+                                                        "/",
+                                                        MALICIOUS_IFACE,
+                                                        MALICIOUS_MEMBER);
+              }
+            else
+              {
+                g_test_message ("Sending malicious method call as a reply");
+                reply = g_dbus_message_new_method_call 
(f->caller_conn.unique_name,
+                                                        "/",
+                                                        MALICIOUS_IFACE,
+                                                        MALICIOUS_MEMBER);
+              }
+            break;
+
+          case G_DBUS_MESSAGE_TYPE_METHOD_RETURN:
+            reply = g_dbus_message_new_method_reply (call);
+
+            if (t->misdirected)
+              {
+                g_test_message ("Sending malicious reply to wrong 
destination");
+                g_dbus_message_set_destination (reply, 
f->cannot_access_conn.unique_name);
+                g_dbus_message_set_body (reply, g_variant_new ("(s)", 
MALICIOUS_BODY));
+              }
+            else
+              {
+                g_test_message ("Sending legitimate reply as a reply");
+              }
+
+            break;
+
+          case G_DBUS_MESSAGE_TYPE_ERROR:
+            reply = g_dbus_message_new_method_error (call,
+                                                     "com.example.No",
+                                                     "That didn't work");
+
+            if (t->misdirected)
+              {
+                g_test_message ("Sending malicious error to wrong 
destination");
+                g_dbus_message_set_destination (reply, 
f->cannot_access_conn.unique_name);
+                g_dbus_message_set_body (reply, g_variant_new ("(s)", 
MALICIOUS_BODY));
+              }
+            else
+              {
+                g_test_message ("Sending legitimate error as a reply");
+              }
+
+            break;
+
+          case G_DBUS_MESSAGE_TYPE_INVALID:
+          default:
+            g_assert_not_reached ();
+        }
+
+      g_dbus_message_set_reply_serial (reply, call_serial);
+      g_dbus_connection_send_message (f->proxied.conn, reply,
+                                      G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+                                      NULL,   /* serial */
+                                      &error);
+      g_assert_no_error (error);
+
+      /* Do another round-trip, to make sure everything has been delivered */
+      do_round_trip (&f->caller_conn, &f->proxied);
+
+      /* The caller didn't receive any extraneous messages */
+      g_assert_cmpint (g_atomic_int_get (&f->caller_conn.n_method_calls), ==,
+                       caller_calls_before);
+      g_assert_cmpint (g_atomic_int_get (&f->caller_conn.n_unicast_signals), 
==,
+                       caller_unicast_before);
+      g_assert_cmpint (g_atomic_int_get (&f->caller_conn.n_broadcasts), ==,
+                       caller_broadcast_before);
+
+      /* The other connection didn't receive any messages at all */
+      g_assert_cmpint (g_atomic_int_get 
(&f->cannot_access_conn.n_method_calls), ==,
+                       other_calls_before);
+      g_assert_cmpint (g_atomic_int_get 
(&f->cannot_access_conn.n_unicast_signals), ==,
+                       other_unicast_before);
+      g_assert_cmpint (g_atomic_int_get (&f->cannot_access_conn.n_broadcasts), 
==,
+                       other_broadcast_before);
+    }
+}
+
 static void
 teardown (Fixture *f,
           gconstpointer context G_GNUC_UNUSED)
@@ -895,6 +1165,7 @@
   g_test_add ("/call", Fixture, NULL, setup, test_call, teardown);
   g_test_add ("/own", Fixture, NULL, setup, test_own, teardown);
   g_test_add ("/receive", Fixture, NULL, setup, test_receive, teardown);
+  g_test_add ("/reply", Fixture, NULL, setup, test_reply, teardown);
 
   return g_test_run ();
 }

Reply via email to