Summary
Multiple authorization bypass vulnerabilities in profile and viewing status management. Core issue: profile operations lack user ownership checks, and the profileId request header is trusted without validation.
Finding 1: Profile IDOR - View/Update/Delete Any User's Profile
File: grails-app/controllers/streama/ProfileController.groovy
show() (line 18), update() (line 43), and delete() (line 62) accept a profile ID but have ZERO ownership checks. Any authenticated user can view, modify, or delete any other user's profile.
// Line 18 - NO ownership check
def show(Profile profile) {
respond profile // Returns any profile by ID
}
// Line 43 - NO ownership check
def update(Profile profile) {
profile.save() // Saves modifications to any profile
respond profile, [status: OK]
}
// Line 62 - NO ownership check
def delete(Profile profile) {
profile.isDeleted = true
profile.save() // Deletes any profile
}
Secure sibling in the SAME file - getUserProfiles() (line 77) correctly filters:
def getUserProfiles() {
def result = Profile.where {
user == springSecurityService.getCurrentUser() // CORRECT
isDeleted != true
}.list()
}
Classic 1-of-N inconsistency: 1 method checks ownership, 4 methods don't.
Finding 2: Mass ViewingStatus Deletion
File: grails-app/controllers/streama/DashController.groovy (lines 233-238)
def markAsCompleted(Video video){
ViewingStatus.where{
video == video // NO user filter!
}.deleteAll() // Deletes viewing status for ALL users
}
Secure sibling - VideoController.markCompleted() correctly gets current user's status:
def markCompleted(Video videoInstance){
ViewingStatus viewingStatus = videoInstance.getViewingStatus() // Current user only
viewingStatus.completed = true
viewingStatus.save()
}
Any authenticated user can wipe the viewing progress of ALL users for any video.
Finding 3: profileId Header Spoofing
Multiple controllers trust the profileId request header without verifying the profile belongs to the current user:
- WatchlistEntryController (lines 16-47, 50-90, 92-117): create/delete/list watchlist entries for any profile
- ViewingStatusController (lines 23-41): create viewing status for any profile
- PlayerController (lines 26-37): update viewing status for any profile
- DashController (lines 13-18): list continue-watching for any profile
Pattern in all affected controllers:
Long profileId = request.getHeader('profileId')?.toLong()
Profile currentProfile = Profile.findById(profileId) // No ownership check!
// ... uses currentProfile without verifying it belongs to currentUser
Impact
- Any user can view/modify/delete other users' profiles (including child/parental control profiles)
- Any user can wipe viewing progress for ALL users on any video
- Any user can read/modify watchlists and viewing history of any profile
- Undermines parental controls and privacy between household members
Suggested Fixes
- Add
profile.user == springSecurityService.currentUser check to show/update/delete
- Add user filter to
markAsCompleted(): user == springSecurityService.currentUser
- Validate profileId header ownership in a shared interceptor
CWE-639 (Authorization Bypass Through User-Controlled Key), CWE-862 (Missing Authorization)
Summary
Multiple authorization bypass vulnerabilities in profile and viewing status management. Core issue: profile operations lack user ownership checks, and the
profileIdrequest header is trusted without validation.Finding 1: Profile IDOR - View/Update/Delete Any User's Profile
File:
grails-app/controllers/streama/ProfileController.groovyshow()(line 18),update()(line 43), anddelete()(line 62) accept a profile ID but have ZERO ownership checks. Any authenticated user can view, modify, or delete any other user's profile.Secure sibling in the SAME file -
getUserProfiles()(line 77) correctly filters:Classic 1-of-N inconsistency: 1 method checks ownership, 4 methods don't.
Finding 2: Mass ViewingStatus Deletion
File:
grails-app/controllers/streama/DashController.groovy(lines 233-238)Secure sibling -
VideoController.markCompleted()correctly gets current user's status:Any authenticated user can wipe the viewing progress of ALL users for any video.
Finding 3: profileId Header Spoofing
Multiple controllers trust the
profileIdrequest header without verifying the profile belongs to the current user:Pattern in all affected controllers:
Impact
Suggested Fixes
profile.user == springSecurityService.currentUsercheck to show/update/deletemarkAsCompleted():user == springSecurityService.currentUserCWE-639 (Authorization Bypass Through User-Controlled Key), CWE-862 (Missing Authorization)