* [WEB-8017] fix(security): sanitize order_by on external REST API list endpoints
Close a partial bypass of WEB-7813 (GHSA-2r95 / GHSA-w45q): the external
REST API project-list and work-item-list endpoints passed a raw order_by
query parameter to Django's .order_by(). Because Django resolves
__-separated relational paths, an attacker could order by sensitive
columns on related tables (created_by__password / token / email) to build
a blind ordering oracle, or crash the endpoint (HTTP 500) with an unknown
field.
Route both endpoints through the existing sanitize_order_by() helper with
the appropriate allowlist (PROJECT_ORDER_BY_ALLOWLIST, default sort_order;
ISSUE_ORDER_BY_ALLOWLIST, default -created_at), mirroring how
order_issue_queryset() already sanitizes. Non-allowlisted values collapse
to the safe default; legitimate orderings are unchanged.
Adds unit tests (allowlist neutralisation + passthrough) and contract
tests asserting both endpoints return 200 (not 500) for injected fields;
fail-before verified via git stash.
Advisory: GHSA-p885-6jpg-cr2p
Co-authored-by: Plane AI <noreply@plane.so>
* [WEB-8019] fix(security): scope CycleIssue reassignment lookup to workspace/project
CycleIssueViewSet.create looked up "issues already in another cycle" with
CycleIssue.objects.filter(~Q(cycle_id=cycle_id), issue_id__in=issues) —
without scoping to the caller's workspace/project. An ADMIN/MEMBER of their
own project could pass a work-item UUID from a different tenant and have that
foreign CycleIssue row reassigned to their cycle, silently evicting the
victim's work item from the victim's cycle (cross-tenant write / BOLA).
Scope the lookup to workspace__slug + project_id, mirroring the adjacent
create-path guard. Foreign-tenant rows are excluded from reassignment and
already dropped from the create path by the scoped new_issues query.
Adds a contract regression test proving a foreign-tenant CycleIssue row is
not reassigned (fail-before verified via git stash) plus a same-project
reassignment test to confirm the legitimate flow is unaffected.
Advisory: GHSA-4w5x-wc9w-f47x
Co-authored-by: Plane AI <noreply@plane.so>
---------
Co-authored-by: Plane AI <noreply@plane.so>
* fix(security): scope issue ID validation to workspace/project in bulk endpoints
Prevents cross-tenant IDOR by filtering incoming issue IDs through
workspace+project scope before bulk_create/bulk_update in:
- CycleIssueListCreateAPIEndpoint: validate new_issues against workspace+project (GHSA-22g9-9xfv-q3fr)
- SubIssuesEndpoint: validate sub_issue_ids against workspace (GHSA-38vj-gf85-7q5x)
- IssueRelationListCreateAPIEndpoint: validate issues against workspace (GHSA-8cvv-8jh5-g6mj)
- ModuleIssueListCreateAPIEndpoint: already scoped at line 673, no change needed (GHSA-x5c5-hmvm-94v9)
Co-authored-by: Plane AI <noreply@plane.so>
* fix(security): extend IDOR scope validation to app-layer endpoints
Same cross-tenant IDOR fix applied to the app/views/ counterparts
which are used by the web frontend (api/views/ covered in previous commit):
- app/views/cycle/issue.py: filter new_issues to workspace+project (GHSA-22g9-9xfv-q3fr)
- app/views/module/issue.py: filter issues to workspace+project before bulk_create (GHSA-x5c5-hmvm-94v9)
- app/views/issue/relation.py: filter issues to workspace before bulk_create (GHSA-8cvv-8jh5-g6mj)
Co-authored-by: Plane AI <noreply@plane.so>
* chore: remove advisory ID references from code comments
---------
Co-authored-by: Plane AI <noreply@plane.so>
Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
* fix: permission check on viewset
* chore: check workspace admin
* chore: initiative is_workspace_admin before if condition
* chore: project member check
* fix: if conditions
* chore: add condition for guests to only edit description and name
* fix: use ROLE enum instead of magic numbers
* chore: remove if condition