Skip to content

Commit 3d5de34

Browse files
fix: enforce authorization on API events/archive action
The REST API archive action toggled an event's Archived (retention-protection) flag with no authorization beyond the controller's coarse "Events permission is not None" gate. Any authenticated read-only user, including one restricted to a subset of monitors, could flip the retention state of any event by enumerating event ids, and the action was reachable over GET (CSRF-able). Gate the write by direction: archiving (protects from purge) requires view access via Event::canView(); un-archiving (re-exposes to purge) requires edit access via Event::canEdit(). Both enforce the per-monitor object-level ACL. Restrict the action to POST/PUT to block CSRF. Addresses GHSA-5v9h-ww7p-hxgv. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent cf92249 commit 3d5de34

1 file changed

Lines changed: 15 additions & 3 deletions

File tree

web/api/app/Controller/EventsController.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -540,15 +540,27 @@ public function archive($id = null) {
540540
throw new NotFoundException(__('Invalid event'));
541541
}
542542

543-
// Get the current value of Archive
543+
// Toggling Archived mutates state, so restrict to state-changing verbs (not CSRF-able GET).
544+
$this->request->allowMethod('post', 'put');
545+
544546
$archived = $this->Event->find('first', array(
545-
'fields' => array('Event.Archived'),
546547
'conditions' => array('Event.Id' => $id)
547548
));
549+
$EventObj = new ZM\Event($archived['Event']);
550+
548551
// If 0, 1, if 1, 0
549552
$archiveVal = (($archived['Event']['Archived'] == 0) ? 1 : 0);
550553

551-
// Save the new value
554+
// Archiving protects an event from purge, so any user who can view the event may do it.
555+
// Un-archiving makes it eligible for purge again, so that requires edit permission.
556+
// Both canView() and canEdit() enforce the per-monitor object-level ACL.
557+
$allowed = $archiveVal ? $EventObj->canView() : $EventObj->canEdit();
558+
if ( !$allowed ) {
559+
throw new UnauthorizedException(__('Insufficient Privileges'));
560+
return;
561+
}
562+
563+
// Save the new value
552564
$this->Event->id = $id;
553565
$this->Event->saveField('Archived', $archiveVal);
554566

0 commit comments

Comments
 (0)