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',
