-
-
Notifications
You must be signed in to change notification settings - Fork 335
Description
Summary
5 authorization vulnerabilities found where Livewire methods can be called directly to bypass UI-only authorization checks. The core issue: several Livewire component methods perform actions on resources identified by client-supplied IDs without server-side authorization verification.
Finding 1: Kanban/Scrum Board IDOR (HIGH)
File: app/Helpers/KanbanScrumHelper.php:157-166
recordUpdated() accepts a ticket ID via Livewire event and updates status/order without authorization:
public function recordUpdated(int $record, int $newIndex, int $newStatus): void
{
$ticket = Ticket::find($record);
if ($ticket) {
$ticket->order = $newIndex;
$ticket->status_id = $newStatus;
$ticket->save();
}
}While getRecords() properly scopes tickets to user's projects, recordUpdated() bypasses this entirely. Any authenticated user can change any ticket's status/order across all projects.
Finding 2: Comment Edit/Delete IDOR (HIGH)
File: app/Filament/Resources/TicketResource/Pages/ViewTicket.php:200-234
editComment() and doDeleteComment() accept comment IDs without authorization:
public function doDeleteComment(int $commentId): void
{
TicketComment::where('id', $commentId)->delete();
}The Blade template (line 274) shows buttons only when $this->isAdministrator() || $comment->user_id === auth()->user()->id, but this is UI-only. Livewire methods can be called directly.
Finding 3: Delete Policies Missing Ownership Check (MEDIUM)
Files: app/Policies/TicketPolicy.php:84, ProjectPolicy.php:78, SprintPolicy.php:78
Delete methods only check Spatie permission, not ownership:
public function delete(User $user, Ticket $ticket) {
return $user->can('Delete ticket');
// Missing: && ($ticket->project->owner_id === $user->id || ...)
}Compare with update() in the same file which correctly checks ownership + project membership.
Finding 4: TimesheetResource Missing Authorization (MEDIUM)
File: app/Filament/Resources/TimesheetResource.php
No TicketHourPolicy exists. ListTimesheet doesn't override getTableQuery() to scope results. All users' timesheet entries visible/editable. Every other list page properly scopes data.
Finding 5: Board/RoadMap Client-Supplied Project IDs (LOW)
Files: app/Filament/Pages/Board.php:70, RoadMap.php:86
search() and filter() accept project IDs from form state without ownership check. Mitigated by downstream Kanban/Scrum mount() authorization for board access, but RoadMap may leak project metadata.
Remediation
- Add authorization checks to all Livewire methods:
public function recordUpdated(int $record, int $newIndex, int $newStatus): void
{
$ticket = Ticket::find($record);
if ($ticket && (
$ticket->project->owner_id === auth()->id() ||
$ticket->project->users->contains('id', auth()->id())
)) {
// ... update
}
}- Add ownership checks to delete policies matching the update policy pattern
- Create TicketHourPolicy and scope TimesheetResource queries
- Server-side validate all Livewire form state (never trust client data)
Found via automated security research by Lighthouse