Skip to content

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

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

[WEB-8400] fix(security): scope DeployBoardViewSet queryset to the URL workspace/project#9481
mguptahub wants to merge 3 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 two earlier reports (mapping on the ticket).

Fixes WEB-8400 (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 or projects.
    • Inaccessible boards now consistently return a not-found response.
  • Tests

    • Added coverage for blocked cross-workspace and cross-project access.
    • Confirmed valid access to the caller’s own deployment boards remains functional.

…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 lite 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: 43da5cdf-ad28-4712-ae5b-15699098e025

📥 Commits

Reviewing files that changed from the base of the PR and between 000175c and 3639e02.

📒 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
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/api/plane/app/views/project/base.py
  • 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 authorized-board retrieval.

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 that foreign and sibling-project boards return HTTP 404 for GET, DELETE, and PATCH requests, remain unchanged, and authorized boards remain accessible.

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

Possibly related PRs

Suggested reviewers: dheeru0198, pablohashescobar

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.46% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the security fix and the queryset scoping change.
Description check ✅ Passed The description clearly explains the vulnerability, fix, tests, and WEB-8400 reference, despite not using every template heading.
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 💡 1
📝 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.

@mguptahub mguptahub changed the title [WEB-8400] fix(security): scope DeployBoardViewSet queryset to the URL workspace/project (GHSA-h4w5-vhxc-265g) [WEB-8400] fix(security): scope DeployBoardViewSet queryset to the URL workspace/project Aug 7, 2026
Explanations kept unchanged; only the IDs are removed.

Co-authored-by: Plane AI <noreply@plane.so>
Copilot AI review requested due to automatic review settings August 7, 2026 10:31
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

React Doctor found 4 issues in 4 files · 1 error & 3 warnings · score 82 / 100 (Needs work) · vs preview

Errors

3 warnings

core/components/issues/issue-layouts/spreadsheet/columns/label-column.tsx

  • ⚠️ L14 Import from a barrel file no-barrel-import

core/components/issues/peek-overview/properties.tsx

  • ⚠️ L42 Import from a barrel file no-barrel-import

core/components/issues/workspace-draft/draft-issue-properties.tsx

  • ⚠️ L31 Import from a barrel file no-barrel-import
⚠️ Warning: .github/workflows/react-doctor.yml is configured incorrectly. See below to fix.

React Doctor compares against preview to report only the issues this pull request introduces. This run couldn't complete that comparison (usually a shallow CI checkout with no merge base), so it listed every issue in the changed files, including ones that already existed on preview.

Add fetch-depth: 0 to the actions/checkout step in .github/workflows/react-doctor.yml so the checkout includes the history React Doctor needs:

 jobs:
   react-doctor:
     steps:
       - uses: actions/checkout@v5
+        with:
+          fetch-depth: 0

       - uses: millionco/react-doctor@v2

To silence this warning, set silence-missing-baseline-warning: true on the React Doctor action.

Reviewed by React Doctor for commit 3639e02. See inline comments for fixes.

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

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