From 2b4b7cdceb8f2c6827e1bd9c739a150d4f3a8ad0 Mon Sep 17 00:00:00 2001 From: AyushShukla1807 Date: Mon, 30 Mar 2026 03:49:33 +0530 Subject: [PATCH] Security: Hardened Juror permissions to use ID and active assignment state --- montage/rdb.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/montage/rdb.py b/montage/rdb.py index b7052ff6..47967c6f 100644 --- a/montage/rdb.py +++ b/montage/rdb.py @@ -2462,7 +2462,7 @@ def __init__(self, user_dao): def _get_round_juror(self, round_id): round_juror = (self.query(RoundJuror) - .filter_by(user=self.user, + .filter_by(user_id=self.user.id, round_id=round_id) .one_or_none()) return round_juror @@ -2471,13 +2471,18 @@ def _get_round_juror(self, round_id): def get_campaign(self, campaign_id): if self.user.is_maintainer: return self.user_dao._get_any_campaign(campaign_id) + + # Security Hardening: We query the association proxy directly to ensure + # the juror is not only assigned by ID, but also strictly active in the round. campaign = self.query(Campaign)\ .filter(Campaign.rounds.any( - Round.jurors.any(username=self.user.username)))\ + Round.round_jurors.any( + user_id=self.user.id, + is_active=True)))\ .filter_by(id=campaign_id)\ .one_or_none() if not campaign: - raise Forbidden('not a juror on campaign %s' % campaign_id) + raise Forbidden('not an active juror on campaign %s' % campaign_id) return campaign def get_round(self, round_id): @@ -2485,11 +2490,13 @@ def get_round(self, round_id): return self.user_dao._get_any_round(round_id) rnd = self.query(Round)\ .filter( - Round.jurors.any(username=self.user.username), + Round.round_jurors.any( + user_id=self.user.id, + is_active=True), Round.id == round_id)\ .one_or_none() if not rnd: - raise Forbidden('not a juror for round %s' % round_id) + raise Forbidden('not an active juror for round %s' % round_id) return rnd def get_round_entry(self, round_id, entry_id):