GHSA-w2ch-4xgr-22ww json
Vikunja: Task relation deletion does not check read access to the other task, allowing cross-project relation removal
published 2026-10-09 30 d ago modified 2026-10-19 20 d ago also GO-2026-6754
- cvss
- —
- not scored
- cisa kev
- Not listed
- not in CISA's catalog
- epss
- —
- no score, EPSS covers CVEs only
- public exploits
- 0
- none known
Summary
## Summary
Deleting a task relation only requires write access on the base task. Unlike relation creation, it does not verify that the caller can read the other task. A user with write access to one project can therefore delete relations whose other end lives in a project they have no access to. Since the delete removes both the forward and inverse rows, the relation also disappears for the other project's members.
## Details
`TaskRelation.CanCreate` (`pkg/models/task_relation_permissions.go:32-52`) requires write access on `TaskID` **and** read access on `OtherTaskID`.
`TaskRelation.CanDelete` (`pkg/models/task_relation_permissions.go:25-29`) only checks `Task{ID: rel.TaskID}.CanUpdate(s, a)`; `OtherTaskID` is never authorized.
`TaskRelation.Delete` (`pkg/models/task_relation.go:314-354`) then deletes both the `(task_id, other_task_id, kind)` row and its inverse, so the relation is removed from the far task as well.
Affects `DELETE /api/v1/tasks/{id}/relations/{kind}/{otherTaskId}` and the equivalent v2 endpoint.
## Impact
Low, integrity only. An authenticated user holding write permission on a shared project can remove task relations that link into projects they cannot read. No data is disclosed and no privilege is gained; the attacker cannot recreate the relation. The far project's owner sees the relation vanish without indication of who removed it.
Preconditions: the attacker has write access to a project containing a task that is already related to a task in a project they cannot access.
## Proof of Concept
1. As `owner`, create project `P_near` with task `near` and project `P_far` with task `far`. 2. Share `P_near` with `attacker` at write permission (`permission: 1`). Do not share `P_far`. 3. As `owner`: `PUT /api/v1/tasks/{near}/relations` with `{"other_task_id": far, "relation_kind": "related"}` -> 200. 4. As `attacker`: `GET /api/v1/tasks/{far}` -> 403 (confirms no access). 5. As `attacker`: `PUT /api/v1/tasks/{near}/relations` with the same body -> 403 (create path is enforced). 6. As `attacker`: `DELETE /api/v1/tasks/{near}/relations/related/{far}` -> 200 `"Successfully deleted."` 7. As `owner`: `GET /api/v1/tasks/{far}` -> `related_tasks` is now empty.
Reproduced against `vikunja/vikunja:2.5.0`.
## Recommended Fix
Make `CanDelete` mirror `CanCreate`: after checking `CanUpdate` on the base task, also require `CanRead` on `OtherTaskID`.
Patch / fix
- go code.vikunja.io/api 2.6.0
- github.com/go-vikunja/vikunja@077dc4d commit
- github.com/go-vikunja/vikunja#3688 pull request
- github.com/go-vikunja/vikunja/releases/tag/v2.6.0 release
Affected packages
| ecosystem | package | affected | fixed in |
|---|---|---|---|
| go | code.vikunja.io/api | >=0.9, <2.6.0 all versions |
2.6.0 |
Classification
References
-
github.com
/go-vikunja/vikunja/pull/3688
web
-
github.com
/go-vikunja/vikunja/commit/077dc4de79ce6f1ab59215a2c7bf9b30423685f2
web
-
github.com
/go-vikunja/vikunja/security/advisories/GHSA-w2ch-4xgr-22ww
web, advisory
-
github.com
/go-vikunja/vikunja
package
-
github.com
/go-vikunja/vikunja/releases/tag/v2.6.0
web
Sources
- GitHub Advisory GHSA-w2ch-4xgr-22ww
- OSV GO-2026-6754
Fencer Vulns keeps one entry per vulnerability: the CVE id wins, then GHSA, and records that alias each other are merged, except that two CVEs always keep separate entries. The same data is available as JSON.