Completed cycles can be edited by including sort_order in the payload
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 59.6k
- Forks
- 5.8k
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 49
Description
Completed cycles can be edited by including sort_order in the payload
What we observed
The cycle update endpoint is intended to lock completed cycles (those with an end_date in the past) so that only sort_order can be changed. However, any field — name, description, dates — can be modified as long as sort_order is included alongside the other fields in the same PATCH request.
1. Completed cycle accepts name and description changes
We created a cycle with start and end dates in the past (already completed). A PATCH request with only {"name": "new name"} was correctly rejected with 400 — "The Cycle has already been completed so it cannot be edited." But when we sent {"sort_order": 1, "name": "new name", "description": "new desc"}, the request succeeded with 200 and the cycle's name and description were both updated. We verified at the database level that the name and description had been overwritten.
Impact
- Completed cycle data can be silently altered, undermining the integrity of historical sprint records
- Auditing and reporting that relies on completed-cycle immutability becomes unreliable
- Any user with write access to cycles can retroactively change cycle metadata after a sprint closes
Steps to reproduce
Environment:
makeplane/plane-backend:stable- PostgreSQL 15.7, Valkey 7.2.11, RabbitMQ 3.13.6, MinIO
ENABLE_EMAIL_PASSWORD=1,ENABLE_SIGNUP=1
Setup:
- Complete instance admin sign-up to bootstrap the Plane instance
- Create a workspace and project
- Create an API token for use with the v1 API
- Create a cycle with both
start_dateandend_datein the past (e.g., 2024-01-01 to 2024-01-14) — this makes the cycle "completed"
The cycle is created via:
POST /api/v1/workspaces/{slug}/projects/{project_id}/cycles/
X-Api-Key: {token}
Content-Type: application/json
{"name": "Sprint Alpha", "description": "Original description", "start_date": "2024-01-01", "end_date": "2024-01-14"}
Control — sort_order-only edit (should succeed):
PATCH /api/v1/workspaces/{slug}/projects/{project_id}/cycles/{cycle_id}/
X-Api-Key: {token}
Content-Type: application/json
{"sort_order": 1}
Returns 200 — sort_order is the one field that should remain editable on completed cycles.
Control — name-only edit (should be rejected):
PATCH /api/v1/workspaces/{slug}/projects/{project_id}/cycles/{cycle_id}/
X-Api-Key: {token}
Content-Type: application/json
{"name": "Attempted Change"}
Returns 400 — "The Cycle has already been completed so it cannot be edited." This confirms the guard is active.
Bug — including sort_order alongside other fields bypasses the guard:
PATCH /api/v1/workspaces/{slug}/projects/{project_id}/cycles/{cycle_id}/
X-Api-Key: {token}
Content-Type: application/json
{"sort_order": 1, "name": "HIJACKED NAME", "description": "HIJACKED DESCRIPTION"}
Returns 200. The cycle's name and description are both updated despite the cycle being completed.
Database verification:
SELECT name, description FROM cycles WHERE id = '{cycle_id}';
Returns name = 'HIJACKED NAME' and description = 'HIJACKED DESCRIPTION', confirming the data was persisted.
Reproducing with Dokkimi
Dokkimi is an open-source testing tool that stands up isolated Docker environments from declarative YAML definitions — services, databases, seed data, test steps, and assertions all in one file. The definition for this bug is in dokkimi/dokkimi-in-the-wild/.dokkimi/plane — dokkimi run reproduces it from scratch.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the PATCH cycle update endpoint described in the reproduction steps and run the supplied Dokkimi definition in dokkimi/dokkimi-in-the-wild/.dokkimi/plane. Compare the sort_order-only, name-only, and combined payload behavior, then verify that completed cycles reject non-sort_order changes while still allowing sort_order updates.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- django, postgresql, python
- Domain
- api, backend, databases
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100