Skip to content

[WEB-7769] fix(security): scope EstimatePoint create/destroy to workspace and project#9286

Open
mguptahub wants to merge 1 commit into
previewfrom
web-7769/fix-estimate-idor
Open

[WEB-7769] fix(security): scope EstimatePoint create/destroy to workspace and project#9286
mguptahub wants to merge 1 commit into
previewfrom
web-7769/fix-estimate-idor

Conversation

@mguptahub

@mguptahub mguptahub commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • GHSA-933r-rxg8-f3h2EstimatePointEndpoint.create: added a workspace+project scoped Estimate ownership check before creating an estimate point. Previously the estimate_id URL parameter was trusted directly, allowing injection into any workspace's estimate.
  • GHSA-933r-rxg8-f3h2 (destroy): scoped the old_estimate_point lookup to estimate_id + project_id + workspace__slug; added 404 guard. Previously fetched by pk only — enabled cross-tenant key disclosure and manipulation during key-rearrangement.
  • GHSA-vm3j-5j49-gwrfBulkEstimatePointEndpoint.partial_update: already correctly scoped (lines 116, 125–130). No change needed.

Files changed

  • apps/api/plane/app/views/estimate/base.py

Test plan

  • Create estimate point with valid estimate_id in same workspace → succeeds
  • Create estimate point with estimate_id from a different workspace → 404
  • Delete estimate point with valid ID → succeeds, keys rearranged correctly
  • Delete estimate point with ID from different workspace → 404, no side effects

Co-authored-by: Plane AI noreply@plane.so

Summary by CodeRabbit

  • Bug Fixes
    • Improved validation for estimate point creation to ensure proper workspace scoping
    • Fixed estimate point deletion to enforce workspace isolation and prevent unauthorized access

…pace and project

GHSA-933r-rxg8-f3h2 — EstimatePointEndpoint.create trusted the
estimate_id URL parameter without verifying it belonged to the caller's
workspace and project. An authenticated user in project A could inject
estimate points into any other workspace's estimate by supplying a
foreign estimate_id.

Fix: added a workspace+project scoped Estimate ownership check before
EstimatePoint.objects.create().

GHSA-933r-rxg8-f3h2 (destroy) — old_estimate_point was fetched with
pk only (unscoped), allowing cross-tenant key disclosure and
manipulation during the key-rearrangement step.

Fix: scoped the old_estimate_point lookup to estimate_id + project_id +
workspace__slug; added 404 guard for missing/foreign points.

Note: BulkEstimatePointEndpoint.partial_update (GHSA-vm3j-5j49-gwrf)
was already correctly scoped at lines 116 and 125-130 — no change needed.

Co-authored-by: Plane AI <noreply@plane.so>
Copilot AI review requested due to automatic review settings June 22, 2026 08:42
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

EstimatePointEndpoint.create and EstimatePointEndpoint.destroy in base.py are patched to scope their database lookups to workspace__slug and project_id, returning 404 responses when the target Estimate or EstimatePoint is not found under the given tenant context.

Changes

Tenant-scoped EstimatePoint IDOR Fix

Layer / File(s) Summary
Scoped Estimate and EstimatePoint lookups
apps/api/plane/app/views/estimate/base.py
create adds a scoped Estimate existence check (filtered by workspace__slug and project_id) returning 404 if absent. destroy replaces an unscoped pk-only EstimatePoint lookup with a query also filtered by estimate_id, project_id, and workspace__slug, returning 404 if the point is not found.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • makeplane/plane#9269: Applies the same cross-tenant IDOR mitigation pattern by adding workspace/project-scoped existence validation before bulk create/update or deletion of related objects (issue, cycle, sub-issue IDs).

Suggested reviewers

  • dheeru0198
  • pablohashescobar
  • sangeethailango

Poem

🐇 A rabbit checks the fence before it hops,
No sneaky tenant slips through any gaps!
workspace__slug guards the carrot store,
project_id locks the burrow door.
404 — those points aren't yours, no more! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main security fix: scoping EstimatePoint create/destroy operations to workspace and project boundaries to prevent IDOR vulnerabilities.
Description check ✅ Passed The description provides detailed information about the security fixes, affected vulnerabilities (GHSA-933r-rxg8-f3h2, GHSA-vm3j-5j49-gwrf), test scenarios, and file changes, though it lacks explicit type-of-change checkbox marking.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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-7769/fix-estimate-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 and usage tips.

@makeplane

makeplane Bot commented Jun 22, 2026

Copy link
Copy Markdown

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

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 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/api/plane/app/views/estimate/base.py (1)

203-239: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate the scoped estimate point before mutating issues.

The new 404 guard runs after issue updates/activity enqueueing. A mismatched estimate_id can still mutate issues or emit activity before returning "Estimate point not found".

Proposed fix
 def destroy(self, request, slug, project_id, estimate_id, estimate_point_id):
     new_estimate_id = request.data.get("new_estimate_id", None)
+
+    # Validate the target before any issue updates or activity side effects.
+    old_estimate_point = EstimatePoint.objects.filter(
+        pk=estimate_point_id,
+        estimate_id=estimate_id,
+        project_id=project_id,
+        workspace__slug=slug,
+    ).first()
+    if not old_estimate_point:
+        return Response(
+            {"error": "Estimate point not found"},
+            status=status.HTTP_404_NOT_FOUND,
+        )
+
     estimate_points = EstimatePoint.objects.filter(
         estimate_id=estimate_id, project_id=project_id, workspace__slug=slug
     )
@@
-    # delete the estimate point — scope to this estimate/project/workspace to prevent cross-tenant key manipulation
-    old_estimate_point = EstimatePoint.objects.filter(
-        pk=estimate_point_id,
-        estimate_id=estimate_id,
-        project_id=project_id,
-        workspace__slug=slug,
-    ).first()
-    if not old_estimate_point:
-        return Response(
-            {"error": "Estimate point not found"},
-            status=status.HTTP_404_NOT_FOUND,
-        )
-
     # rearrange the estimate points

Also applies to: 241-252

🤖 Prompt for 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.

In `@apps/api/plane/app/views/estimate/base.py` around lines 203 - 239, The code
validates the scoped estimate point after mutating issues and enqueueing
activity logs, allowing invalid or mismatched estimate IDs to cause unintended
mutations. Move the validation logic that checks whether the estimate point
exists and belongs to the project and workspace to execute before the
conditional blocks that filter issues and call issue_activity.delay, ensuring
that a 404 response is returned before any issue updates or activity logs are
created when an invalid estimate_point_id or new_estimate_id is provided.
🤖 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.

Outside diff comments:
In `@apps/api/plane/app/views/estimate/base.py`:
- Around line 203-239: The code validates the scoped estimate point after
mutating issues and enqueueing activity logs, allowing invalid or mismatched
estimate IDs to cause unintended mutations. Move the validation logic that
checks whether the estimate point exists and belongs to the project and
workspace to execute before the conditional blocks that filter issues and call
issue_activity.delay, ensuring that a 404 response is returned before any issue
updates or activity logs are created when an invalid estimate_point_id or
new_estimate_id is provided.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 16adca86-0c7f-4ec4-a504-a535f667000e

📥 Commits

Reviewing files that changed from the base of the PR and between 4a0746b and 1e10df7.

📒 Files selected for processing (1)
  • apps/api/plane/app/views/estimate/base.py

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