This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit b763ed7c6dac7ebcd8420f52a3d2567114d22013
Author: Dave Brondsema <[email protected]>
AuthorDate: Fri May 15 18:10:57 2026 -0400

    [#8607] check mailbox owner
---
 Allura/allura/controllers/auth.py           |  5 +++--
 Allura/allura/tests/functional/test_auth.py | 29 +++++++++++++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/Allura/allura/controllers/auth.py 
b/Allura/allura/controllers/auth.py
index 4bc05f5b3..6ff0c7577 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -1378,8 +1378,9 @@ def update_subscriptions(self, subscriptions=None, 
email_format=None, **kw):
                         project_id=bson.ObjectId(s['project_id']),
                         app_config_id=bson.ObjectId(s['tool_id']))
             else:
-                if s['subscription_id'] is not None:
-                    s['subscription_id'].delete()
+                mbox = s['subscription_id']
+                if mbox is not None and mbox.user_id == c.user._id:
+                    mbox.delete()
         if email_format:
             c.user.set_pref('email_format', email_format)
 
diff --git a/Allura/allura/tests/functional/test_auth.py 
b/Allura/allura/tests/functional/test_auth.py
index 78027e116..347ec996e 100644
--- a/Allura/allura/tests/functional/test_auth.py
+++ b/Allura/allura/tests/functional/test_auth.py
@@ -984,6 +984,35 @@ def test_prefs_subscriptions_unsubscribe(self):
         s = M.Mailbox.query.get(_id=s_id)
         assert not s, "User still has subscription with Mailbox._id %s" % s_id
 
+    @td.with_user_project('test-admin')
+    def test_update_subscriptions_rejects_foreign_mailbox(self):
+        # Create a Mailbox owned by test-user-1 (the victim).
+        victim = M.User.by_username('test-user-1')
+        project = M.Project.query.get(shortname='test')
+        app_config = project.app_configs[0]
+        victim_mailbox = M.Mailbox(
+            user_id=victim._id,
+            project_id=project._id,
+            app_config_id=app_config._id)
+        ThreadLocalODMSession.flush_all()
+        victim_mailbox_id = victim_mailbox._id
+
+        # Attacker (test-admin) loads their own subscriptions form, then swaps 
in
+        # the victim's Mailbox _id and submits with subscribed unchecked.
+        resp = self.app.get('/auth/subscriptions/',
+                            extra_environ=dict(username='test-admin'))
+        form = self._find_subscriptions_form(resp)
+        field_name = self._find_subscriptions_field(form, subscribed=True)
+        form.fields[field_name + '.subscription_id'][0].value = 
str(victim_mailbox_id)
+        form.fields[field_name + '.subscribed'][0].value = None
+        form.submit(extra_environ=dict(username='test-admin'))
+
+        # Victim's mailbox must still exist.
+        still_there = M.Mailbox.query.get(_id=victim_mailbox_id)
+        assert still_there is not None, \
+            "victim's Mailbox was deleted via cross-user update_subscriptions"
+        assert still_there.user_id == victim._id
+
     def test_format_email(self):
         self.app.get('/').follow()  # establish session
         self.app.post('/auth/subscriptions/update_subscriptions',

Reply via email to