Skip to content

[WEB-8291] fix: gate project invitation list/retrieve/destroy to project admins (GHSA-r68c-48rr-m67f)#9444

Open
mguptahub wants to merge 1 commit into
previewfrom
web-8291/project-invite-list-admin-scope
Open

[WEB-8291] fix: gate project invitation list/retrieve/destroy to project admins (GHSA-r68c-48rr-m67f)#9444
mguptahub wants to merge 1 commit into
previewfrom
web-8291/project-invite-list-admin-scope

Conversation

@mguptahub

@mguptahub mguptahub commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes the confirmed-vulnerable half of GHSA-r68c-48rr-m67f (HIGH, CWE-862) — the project-invitation-list authorization bypass.

ProjectInvitationsViewset declared no permission_classes (inheriting IsAuthenticated) and only decorated create with @allow_permission([ROLE.ADMIN]). The default list / retrieve / destroy actions were therefore ungated, and get_queryset was scoped only by URL slug + project_id — not by the caller's membership. The serializer (ProjectMemberInviteSerializer, fields="__all__") returns email and token.

Impact (before fix): any authenticated user could GET /api/workspaces/<slug>/projects/<project_id>/invitations/ (and /<pk>/) for a project they don't belong to and read every pending invite's raw token + invitee email (re-exposing what #9305 stripped from the public path), and could DELETE arbitrary project invitations.

The other vector in the advisory — unauthenticated force-accept — is already remediated: ProjectJoinEndpoint.post enforces token + auth + email-match (#9308) and the public GET uses the token-stripped serializer (#9305).

Fix

Gate list / retrieve / destroy with @allow_permission([ROLE.ADMIN]), matching create (project admin, or a workspace-admin who is a project member — the decorator's existing semantics). This mirrors the workspace sibling WorkspaceInvitationsViewset, which was already admin-gated (WorkSpaceAdminPermission).

Chose the method decorator over a class-level ProjectAdminPermission deliberately: a class-level permission would have tightened create (blocking the workspace-admin-project-member fallback that @allow_permission allows), a behavior change. The decorator keeps all four actions consistent.

Also removes a pre-existing unused User import in the same file.

Tests

New contract suite plane/tests/contract/app/test_project_invite_list_scope_app.py (fail-before verified — the 3 security tests return 200/204 without the fix):

  • A workspace user who is a member of a different project is rejected with 403 on list / retrieve / delete, and the invitation is left intact.
  • Positive control: an active project admin can still list invitations and sees the pending invite.

Verification

  • ruff check: clean
  • manage.py check (test settings): no issues
  • pytest (new suite): 4/4 green; fail-before verified

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Restricted project invitation listing, viewing, and deletion to project administrators.
    • Prevented unauthorized workspace users from accessing invitations belonging to other projects.
    • Confirmed that project administrators can continue managing pending invitations.
  • Tests

    • Added coverage for authorized and unauthorized invitation access scenarios.

@mguptahub
mguptahub requested a review from dheeru0198 as a code owner July 20, 2026 06:21
Copilot AI review requested due to automatic review settings July 20, 2026 06:21
@makeplane

makeplane Bot commented Jul 20, 2026

Copy link
Copy Markdown

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e6136e06-a751-44f5-a808-a58fbc4169fe

📥 Commits

Reviewing files that changed from the base of the PR and between 82a315e and e78665f.

📒 Files selected for processing (2)
  • apps/api/plane/app/views/project/invite.py
  • apps/api/plane/tests/contract/app/test_project_invite_list_scope_app.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/api/plane/tests/contract/app/test_project_invite_list_scope_app.py
  • apps/api/plane/app/views/project/invite.py

📝 Walkthrough

Walkthrough

Project invitation list, retrieve, and delete endpoints now require project-admin permissions. Contract tests cover denial for non-members of the target project and successful listing by an active project administrator.

Changes

Project invitation authorization

Layer / File(s) Summary
Admin-only invitation actions
apps/api/plane/app/views/project/invite.py, apps/api/plane/tests/contract/app/test_project_invite_list_scope_app.py
Invitation list, retrieve, and delete actions enforce project-admin access, remove an unused import, and verify outsider 403_FORBIDDEN responses plus administrator listing access.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • makeplane/plane#9373: Tightens project-scoped authorization for workspace cycles and modules with contract tests.

Suggested reviewers: pablohashescobar

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the authorization fix for project invitation actions and matches the changed files.
Description check ✅ Passed The description is detailed and covers the bug, fix, tests, and verification, though it doesn't use the repo's exact template headings.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch web-8291/project-invite-list-admin-scope

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens app API authorization around two previously vulnerable endpoints: project invitation list/retrieve/delete (GHSA-r68c-48rr-m67f / WEB-8291) and draft-to-issue conversion ownership scoping (GHSA-vfqm-7rh7-84hq / WEB-8289).

Changes:

  • Restricts ProjectInvitationsViewset list / retrieve / destroy actions to project admins via @allow_permission([ROLE.ADMIN]).
  • Scopes WorkspaceDraftIssueViewSet.create_draft_to_issue draft lookup to created_by=request.user and returns 404 when the draft isn’t owned by the caller.
  • Adds contract test coverage for both authorization/ownership scenarios.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
apps/api/plane/app/views/project/invite.py Gates invitations list/retrieve/destroy to project admins to prevent cross-project invite/token disclosure and deletion.
apps/api/plane/app/views/workspace/draft.py Prevents non-owners from converting (and thereby consuming/migrating assets from) another user’s draft.
apps/api/plane/tests/contract/app/test_project_invite_list_scope_app.py Contract regression tests ensuring non-members get 403 and project admins can still list invites.
apps/api/plane/tests/contract/app/test_draft_to_issue_owner_scope_app.py Contract regression tests ensuring only the draft owner can convert; non-owners receive 404 and cannot steal assets.

Comment thread apps/api/plane/app/views/workspace/draft.py Outdated
…ect admins (GHSA-r68c-48rr-m67f)

ProjectInvitationsViewset declared no permission_classes (inheriting
IsAuthenticated) and only decorated create with @allow_permission([ADMIN]).
The default list/retrieve/destroy actions were ungated and get_queryset
was scoped only by URL slug + project_id, so any authenticated user could
read another project's pending invitations — including invitee email and
the raw token (re-exposing what #9305 stripped from the public path) — and
delete them.

Gate list/retrieve/destroy with @allow_permission([ROLE.ADMIN]), matching
create (project admin, or a workspace-admin who is a project member). The
workspace sibling WorkspaceInvitationsViewset was already admin-gated.
Also drop a pre-existing unused User import in the same file.

Adds contract regression tests (fail-before verified): a non-member of the
project is rejected with 403 on list/retrieve/destroy (invite left intact),
with a positive control confirming a project admin can still list.

Co-authored-by: Plane AI <noreply@plane.so>
@mguptahub
mguptahub force-pushed the web-8291/project-invite-list-admin-scope branch from 82a315e to e78665f Compare July 20, 2026 06:30
@mguptahub

Copy link
Copy Markdown
Collaborator Author

Good catch @copilot — that was a branching mistake, not intended scope. web-8291 was accidentally cut from the web-8289 branch instead of preview, so it carried the WEB-8289 draft-to-issue commits (which have their own PR, #9443).

Fixed by rebasing web-8291 onto preview (git rebase --onto origin/preview), dropping the two WEB-8289 commits. This PR now contains only the WEB-8291 project-invitation authorization fix:

  • apps/api/plane/app/views/project/invite.py
  • apps/api/plane/tests/contract/app/test_project_invite_list_scope_app.py

The draft-to-issue change stays tracked/reviewed independently in #9443. 4/4 invite contract tests still green; PR is mergeable.

Copilot AI commented Jul 20, 2026

Copy link
Copy Markdown

Good catch @copilot — that was a branching mistake, not intended scope. web-8291 was accidentally cut from the web-8289 branch instead of preview, so it carried the WEB-8289 draft-to-issue com...

Confirmed — the branch now contains only the WEB-8291 project-invitation changes (apps/api/plane/app/views/project/invite.py + contract tests). The WEB-8289 draft-to-issue commits are gone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants