Skip to content

[WEB-8400] fix(security): scope DeployBoardViewSet queryset to the URL workspace/project (GHSA-h4w5-vhxc-265g) - #9481

Open
mguptahub wants to merge 2 commits into
previewfrom
web-8400/deploy-board-cross-workspace-idor
Open

[WEB-8400] fix(security): scope DeployBoardViewSet queryset to the URL workspace/project (GHSA-h4w5-vhxc-265g)#9481
mguptahub wants to merge 2 commits into
previewfrom
web-8400/deploy-board-cross-workspace-idor

Conversation

@mguptahub

@mguptahub mguptahub commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

DeployBoardViewSet (apps/api/plane/app/views/project/base.py) defines only list/create. The routed retrieve/partial_update/destroy (.../project-deploy-boards/<pk>/) are not defined on the class and fall through to DRF's ModelViewSet defaults, which resolve the object via get_object()get_queryset(). The base get_queryset returns DeployBoard.objects.all() (every workspace), and ProjectMemberPermission only reads the URL slug/project_id — nothing binds the object to that scope, and no has_object_permission exists.

So any authenticated user could put their own workspace+project in the path and a victim board's pk as the target to read, modify, or hard-delete any workspace's published board. The victim pk is disclosed unauthenticated by the public board-settings endpoint (fields="__all__"). Same defect class as GHSA-rfj3-8c85-g46j / GHSA-4q54-h4x9-m329.

Fixes GHSA-h4w5-vhxc-265g (CWE-639/862). Confirmed vulnerable against origin/preview @ a8e53b6ac7.

Fix

Override get_queryset on DeployBoardViewSet to scope to the URL workspace__slug + project (entity_name="project", entity_identifier/project_id = URL project). get_object() then 404s for any board outside the caller's URL scope. list/create are unaffected (they build their own queries).

Tests

test_deploy_board_cross_workspace_scope_app.py — cross-workspace retrieve (404), destroy (404 + board still exists), patch (404 + unchanged); own board retrievable (200). Fail-before verified; ruff clean.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Deployment boards are now correctly scoped to the selected workspace and project.
    • Prevented unauthorized retrieval, updating, or deletion of boards from other workspaces/projects.
    • Inaccessible boards now consistently return a not-found response.
  • Tests

    • Added contract tests to verify cross-workspace/project access is blocked for retrieve, patch, and destroy.
    • Added a positive control confirming valid scoped access still works.

…L workspace/project (GHSA-h4w5-vhxc-265g)

DeployBoardViewSet defines only list/create; the routed retrieve/partial_update/
destroy fall through to DRF's ModelViewSet defaults, which resolve the object via
get_object() -> get_queryset(). The base get_queryset returns
DeployBoard.objects.all() (every workspace), and ProjectMemberPermission only
checks the URL slug/project_id — nothing binds the object to that scope. So any
authenticated user could supply their own workspace+project in the URL and a
victim board's pk (disclosed unauth via the public settings endpoint) to read,
modify, or hard-delete any workspace's published board.

Override get_queryset to scope to the URL workspace__slug + project so a foreign
pk 404s. Adds 4 contract tests (cross-workspace retrieve/destroy/patch blocked,
own board retrievable); fail-before verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mguptahub
mguptahub requested a review from dheeru0198 as a code owner July 27, 2026 06:37
Copilot AI review requested due to automatic review settings July 27, 2026 06:37

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 27, 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 Plus

Run ID: a4106f80-ea08-4e71-86e5-d1cf097a69ae

📥 Commits

Reviewing files that changed from the base of the PR and between 834ee3c and 000175c.

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

📝 Walkthrough

Walkthrough

DeployBoardViewSet now scopes deploy board lookups to the URL workspace and project. Contract tests cover foreign-board retrieval, deletion, updates, same-workspace cross-project access, and successful access to an authorized board.

Changes

Deploy board access scoping

Layer / File(s) Summary
Scoped deploy board queryset
apps/api/plane/app/views/project/base.py
DeployBoardViewSet.get_queryset() filters boards by workspace slug, project entity, entity identifier, and project ID.
Cross-workspace access contract tests
apps/api/plane/tests/contract/app/test_deploy_board_cross_workspace_scope_app.py
Fixtures and tests verify foreign and sibling-project boards return HTTP 404 for GET, DELETE, and PATCH requests, remain unchanged, and own boards remain accessible.

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

Possibly related PRs

  • makeplane/plane#9333: Tightens workspace scoping for project-related API operations in a different viewset.
  • makeplane/plane#9442: Tightens workspace and project scoping for public Spaces board endpoints.

Suggested reviewers: dheeru0198

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the security fix and the scoped DeployBoardViewSet queryset change.
Description check ✅ Passed The description is detailed and covers the change, impact, tests, and references; only non-critical template sections are missing.
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-8400/deploy-board-cross-workspace-idor

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.

@makeplane

makeplane Bot commented Jul 27, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/api/plane/tests/contract/app/test_deploy_board_cross_workspace_scope_app.py`:
- Around line 53-64: Extend the contract tests around the existing victim_board
and project-scoped board URL to create a second project within the caller’s
workspace, add a board for that project, and assert the project_a URL returns
404 for it. Keep the test setup using the same workspace while ensuring the
endpoint remains constrained by both workspace slug and URL project identifier.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c9fcb447-d205-4b1f-8abc-ef6e31fcc787

📥 Commits

Reviewing files that changed from the base of the PR and between a8e53b6 and 834ee3c.

📒 Files selected for processing (2)
  • apps/api/plane/app/views/project/base.py
  • apps/api/plane/tests/contract/app/test_deploy_board_cross_workspace_scope_app.py

…Rabbit #9481)

The negative cases all used a different workspace, so they'd pass even if the
project predicate were dropped from get_queryset (workspace slug alone would
catch them). Add a board for a different project in the SAME workspace and assert
it 404s via project_a's URL, guarding the project_id/entity_identifier scope.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 27, 2026 07:05

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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.

2 participants