Skip to content

Security: Cross-project ticket manipulation via Livewire + delete policy missing ownership checks #141

@lighthousekeeper1212

Description

@lighthousekeeper1212

Summary

A security audit identified 12 authorization vulnerabilities (1 Critical, 6 High, 5 Medium) in the project management system.

Critical Finding

Kanban/Scrum Board — Cross-Project Ticket Status IDOR (CRITICAL)

app/Helpers/KanbanScrumHelper.php:157-166:

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();
    }
}

This Livewire listener accepts a ticket ID from the client with zero ownership or project membership verification. While the parent Kanban/Scrum page scopes to the user's project, this method can be called via the Livewire wire protocol with ANY ticket ID — enabling cross-project ticket status manipulation.

HIGH Findings — 1-of-N Policy Pattern

Delete Policies Missing Ownership Checks

Three policies (Ticket, Project, Sprint) all have the same pattern — view and update verify project membership/ownership, but delete only checks the Spatie permission:

TicketPolicy.php:84-87:

public function delete(User $user, Ticket $ticket) {
    return $user->can('Delete ticket'); // NO ownership check
}

Compare with update() which checks $ticket->owner_id === $user->id OR $ticket->responsible_id === $user->id OR project membership. Since the default role includes all permissions, any authenticated user can delete any ticket/project/sprint.

Comment Edit/Delete Missing Ownership (HIGH)

ViewTicket.php:170,229submitComment() and doDeleteComment() accept comment IDs without verifying the comment belongs to the current ticket or that the user is the author. Any user can edit/delete any comment system-wide.

Timesheet Resource — No Policy (HIGH)

TimesheetResource.php has NO associated policy. Any authenticated user can list ALL users' timesheets, edit any entry, and bulk-delete entries across all projects.

Epic CRUD Missing Project Auth (HIGH)

EpicForm.php — Livewire component performs no authorization checks on epic create/edit/delete.

MEDIUM — UI-Only Auth Pattern

ManageGeneralSettings, TimesheetDashboard, TimesheetExport, and JiraImport pages all use shouldRegisterNavigation() to hide nav links — but this does NOT prevent direct URL access. Any authenticated user can modify platform-wide settings.

Dashboard widgets (TicketTimeLogged, TicketsByPriority, TicketsByType, UserTimeLogged) query ALL tickets without project scoping — leaking cross-project aggregate data.

Recommended Fixes

  1. Add authorization to recordUpdated() — verify user is a project member before modifying the ticket
  2. Add ownership checks to delete policies to match view/update patterns
  3. Create a TicketHourPolicy for the Timesheet resource
  4. Add mount() authorization to Filament pages instead of relying on shouldRegisterNavigation()
  5. Scope dashboard widget queries to the current user's projects

Disclosure

Filed in good faith. No exploit code provided.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions