From 11cc85fea2b478438f61a98931e05b800af074ca Mon Sep 17 00:00:00 2001 From: Harsh Vardhan Date: Thu, 13 Aug 2026 19:01:37 +0000 Subject: [PATCH] fix(analytics): scope Cycle and Module lookups by workspace to prevent cross-workspace data leak The analytics charts endpoint accepts cycle_id and module_id as query params and fetches rows using only the bare primary key with no workspace constraint. A member of Workspace A can supply a cycle or module UUID from Workspace B and receive its start_date and end_date. Permission validation only confirms the caller is a member of the requesting workspace; it does not verify the supplied IDs belong to that workspace. Fix: add workspace__slug=self._workspace_slug to both filter calls so a foreign ID returns None, which the existing guard converts to an empty response. Fixes makeplane/plane#9601 Signed-off-by: harsh4vardhan --- apps/api/plane/app/views/analytic/project_analytics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api/plane/app/views/analytic/project_analytics.py b/apps/api/plane/app/views/analytic/project_analytics.py index 064e556a2cd..f93a05f22ad 100644 --- a/apps/api/plane/app/views/analytic/project_analytics.py +++ b/apps/api/plane/app/views/analytic/project_analytics.py @@ -193,7 +193,7 @@ def work_item_completion_chart(self, project_id, cycle_id=None, module_id=None) cycle_issues = CycleIssue.objects.filter(**self.filters["base_filters"], cycle_id=cycle_id).values_list( "issue_id", flat=True ) - cycle = Cycle.objects.filter(id=cycle_id).first() + cycle = Cycle.objects.filter(id=cycle_id, workspace__slug=self._workspace_slug).first() if cycle and cycle.start_date: start_date = cycle.start_date.date() end_date = cycle.end_date.date() @@ -205,7 +205,7 @@ def work_item_completion_chart(self, project_id, cycle_id=None, module_id=None) module_issues = ModuleIssue.objects.filter(**self.filters["base_filters"], module_id=module_id).values_list( "issue_id", flat=True ) - module = Module.objects.filter(id=module_id).first() + module = Module.objects.filter(id=module_id, workspace__slug=self._workspace_slug).first() if module and module.start_date: start_date = module.start_date end_date = module.target_date