fix(api): scope cross-workspace resource lookups to prevent IDOR#9008
Conversation
`ProjectViewSet.partial_update`, `BulkEstimatePointEndpoint.partial_update`, and `WorkspaceUserProfileEndpoint.get` previously fetched objects by primary key alone after a workspace-scoped permission check, allowing an authenticated caller to act on resources belonging to other workspaces by supplying a foreign UUID with their own workspace slug in the URL. - Project partial_update: scope `Project.objects.get` by `workspace__slug`, matching the existing pattern in `destroy`. - Bulk estimate partial_update: scope `Estimate.objects.get` by `workspace__slug` and `project_id`, matching `retrieve` and `destroy`. - Workspace user profile: require the target `user_id` to be an active member of the requested workspace before returning email and other PII.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThree API endpoints add stricter scope validation to their resource lookups: ChangesScope Tightening for Resource Access Control
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |
There was a problem hiding this comment.
Pull request overview
This PR hardens several workspace-scoped “app” endpoints against IDOR by ensuring resource lookups are constrained to the workspace (and project where applicable), preventing authenticated users from operating on cross-workspace resources by UUID.
Changes:
- Scope
ProjectViewSet.partial_updateproject lookup byworkspace__slug. - Scope
BulkEstimatePointEndpoint.partial_updateestimate lookup byworkspace__slugandproject_id. - Ensure
WorkspaceUserProfileEndpoint.getonly returns profile data for users who are active members of the requested workspace (fetching the user via the membership row).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| apps/api/plane/app/views/workspace/user.py | Prevents cross-workspace profile access by requiring an active workspace membership for the target user. |
| apps/api/plane/app/views/project/base.py | Prevents cross-workspace project mutation by scoping the project lookup to the workspace slug. |
| apps/api/plane/app/views/estimate/base.py | Prevents cross-workspace estimate mutation by scoping estimate lookup to workspace + project. |
Summary
Three endpoints fetched objects by primary key alone after a workspace-scoped permission check. An authenticated caller could act on resources in other workspaces by supplying a foreign UUID together with their own workspace slug in the URL.
ProjectViewSet.partial_update(apps/api/plane/app/views/project/base.py:335): A workspace admin could PATCH any project's name, identifier, description, network/visibility, default assignee, etc. across workspaces. Scope theProject.objects.get(...)lookup byworkspace__slug=slug, matching the pattern already used indestroy(line 393).BulkEstimatePointEndpoint.partial_update(apps/api/plane/app/views/estimate/base.py:116): Any project member could rename or retype estimates in foreign workspaces. Scope theEstimate.objects.get(...)lookup byworkspace__slugandproject_id, matchingretrieve(line 104) anddestroy(line 148).WorkspaceUserProfileEndpoint.get(apps/api/plane/app/views/workspace/user.py:282): Any authenticated workspace member could read another user's email, name, avatar, timezone, and join date by UUID, regardless of whether the target user belonged to that workspace. Require the targetuser_idto be an activeWorkspaceMemberof the requested workspace before exposing profile data; fetch the user via the membership row in a single query.The reported logic flaw in the
partial_updatepermission guard (workspace admins bypass the project-admin check) is intentional behavior in this codebase — workspace admins do have project-edit authority. The bug was strictly the unscoped object lookup; once the project fetch is bound to the workspace slug, an admin of workspace A cannot reach into workspace B even if the guard is satisfied.Test plan
PATCH /api/v1/workspaces/workspace-a/projects/{workspace-B-project-uuid}/with a body changingname. Expect 404 (was 200 + mutation).PATCH /api/v1/workspaces/workspace-a/projects/{workspace-A-project-uuid}/estimates/{workspace-B-estimate-uuid}/. Expect 404 (was 200 + mutation).GET /api/v1/workspaces/workspace-a/user-profile/{user-not-in-workspace-A-uuid}/. Expect 404 (was 200 + PII).Summary by CodeRabbit
Release Notes