-
-
Notifications
You must be signed in to change notification settings - Fork 571
Description
Summary
Multiple controllers use Model.find(params[:id]) without scoping to current_organization, allowing authenticated users from one organization to read and modify data belonging to other organizations.
Affected Controllers
1. RequestsController (state-changing)
show(line 31):Request.find(params[:id])— cross-org readstart(line 45):Request.find(params[:id])+request.status_started!— cross-org state change
Compare with index (line 6) which properly uses current_organization.ordered_requests.
2. DonationsController (data modification)
print(line 6):Donation.find(params[:id])— cross-org PDF generationshow(line 66):Donation.find(params[:id])— cross-org readedit(line 56):Donation.find(params[:id])— cross-org readupdate(line 71):Donation.find(params[:id])— cross-org modification
Compare with destroy (line 90) which properly uses current_organization.id.
3. KitsController (inventory manipulation)
deactivate(line 47):Kit.find(params[:id])— cross-org state changereactivate(line 53):Kit.find(params[:id])— cross-org state changeallocations(line 63):Kit.find(params[:id])— cross-org readallocate(line 71):Kit.find(params[:id])— cross-org inventory modification
Compare with index (line 7) which properly uses current_organization.kits.
4. BroadcastAnnouncementsController
set_broadcast_announcement(line 48):BroadcastAnnouncement.find(params[:id])— affects edit/update/destroy
Compare with index (line 5) which properly filters by organization_id: current_organization.id.
5. DistributionsController
print(line 17):Distribution.find(params[:id])— cross-org PDF with partner data
Root Cause
The authorize_user method in ApplicationController checks that the user has a valid role for some organization, but doesn't enforce that the accessed record belongs to the user's organization. Controllers that use Model.find(params[:id]) instead of current_organization.model_name.find(params[:id]) bypass organization isolation.
Fix
Replace Model.find(params[:id]) with current_organization.model_name.find(params[:id]) in all affected actions:
# Before (vulnerable)
@request = Request.find(params[:id])
# After (fixed)
@request = current_organization.requests.find(params[:id])This pattern is already correctly used in many other controllers and some actions within the affected controllers themselves.
Found via automated security audit. CWE-639: Authorization Bypass Through User-Controlled Key.