Bug Description
The advance analytics charts endpoint looks up Cycle and Module by bare ID with no workspace scope. An authenticated user in workspace A can read start_date and end_date from cycles and modules belonging to workspace B by passing foreign UUIDs in the query parameters.
Affected file
�pps/api/plane/app/views/analytic/project_analytics.py:
`python
cycle = Cycle.objects.filter(id=cycle_id).first() # line 196 - no workspace filter
...
module = Module.objects.filter(id=module_id).first() # line 208 - no workspace filter
`
The endpoint is protected by @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) which validates the caller's membership in the requested workspace. The CycleIssue query is correctly scoped via �ase_filters (which includes workspace__slug=slug), so it returns no data for foreign cycle IDs. But the bare Cycle.objects.filter(id=...) lookup succeeds for any cycle UUID in the database, and the response includes cycle.start_date and cycle.end_date directly in the chart metadata.
Failure scenario
- User in workspace A calls GET /workspaces/workspace-a/projects/<any_id>/advance-analytics-charts/?type=work-items&cycle_id=<UUID_from_workspace_B>.
- The permission check passes (user is a member of workspace A).
- Cycle.objects.filter(id=<UUID_from_workspace_B>).first() returns the foreign cycle object.
- start_date and end_date from workspace B's cycle are included in the API response.
- By iterating UUIDs, an attacker can enumerate cycle date ranges across all workspaces.
Fix
python cycle = Cycle.objects.filter(id=cycle_id, workspace__slug=slug).first() module = Module.objects.filter(id=module_id, workspace__slug=slug).first()
Environment
Plane develop branch (2026-08-13).
Bug Description
The advance analytics charts endpoint looks up Cycle and Module by bare ID with no workspace scope. An authenticated user in workspace A can read start_date and end_date from cycles and modules belonging to workspace B by passing foreign UUIDs in the query parameters.
Affected file
�pps/api/plane/app/views/analytic/project_analytics.py:
`python
cycle = Cycle.objects.filter(id=cycle_id).first() # line 196 - no workspace filter
...
module = Module.objects.filter(id=module_id).first() # line 208 - no workspace filter
`
The endpoint is protected by @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) which validates the caller's membership in the requested workspace. The CycleIssue query is correctly scoped via �ase_filters (which includes workspace__slug=slug), so it returns no data for foreign cycle IDs. But the bare Cycle.objects.filter(id=...) lookup succeeds for any cycle UUID in the database, and the response includes cycle.start_date and cycle.end_date directly in the chart metadata.
Failure scenario
Fix
python cycle = Cycle.objects.filter(id=cycle_id, workspace__slug=slug).first() module = Module.objects.filter(id=module_id, workspace__slug=slug).first()Environment
Plane develop branch (2026-08-13).