security: analytics charts endpoint leaks cross-workspace cycle and module dates via unscoped ID lookup
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 59.6k
- Forks
- 5.8k
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 49
Description
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).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in project_analytics.py at the advance analytics charts endpoint and review the Cycle and Module lookups alongside the existing workspace-scoped filters. Reproduce the foreign-workspace UUID scenario, then verify that the response no longer exposes chart metadata from another workspace using the endpoint's relevant tests if available.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- django, python
- Domain
- backend-api-design, security
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100