{"id":"GHSA-w2ch-4xgr-22ww","aliases":["GO-2026-6754"],"status":"published","title":"Vikunja: Task relation deletion does not check read access to the other task, allowing cross-project relation removal","description":"## Summary\n\nDeleting 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.\n\n## Details\n\n`TaskRelation.CanCreate` (`pkg/models/task_relation_permissions.go:32-52`) requires write access on `TaskID` **and** read access on `OtherTaskID`.\n\n`TaskRelation.CanDelete` (`pkg/models/task_relation_permissions.go:25-29`) only checks `Task{ID: rel.TaskID}.CanUpdate(s, a)`; `OtherTaskID` is never authorized.\n\n`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.\n\nAffects `DELETE /api/v1/tasks/{id}/relations/{kind}/{otherTaskId}` and the equivalent v2 endpoint.\n\n## Impact\n\nLow, 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.\n\nPreconditions: the attacker has write access to a project containing a task that is already related to a task in a project they cannot access.\n\n## Proof of Concept\n\n1. As `owner`, create project `P_near` with task `near` and project `P_far` with task `far`.\n2. Share `P_near` with `attacker` at write permission (`permission: 1`). Do not share `P_far`.\n3. As `owner`: `PUT /api/v1/tasks/{near}/relations` with `{\"other_task_id\": far, \"relation_kind\": \"related\"}` -\u003e 200.\n4. As `attacker`: `GET /api/v1/tasks/{far}` -\u003e 403 (confirms no access).\n5. As `attacker`: `PUT /api/v1/tasks/{near}/relations` with the same body -\u003e 403 (create path is enforced).\n6. As `attacker`: `DELETE /api/v1/tasks/{near}/relations/related/{far}` -\u003e 200 `\"Successfully deleted.\"`\n7. As `owner`: `GET /api/v1/tasks/{far}` -\u003e `related_tasks` is now empty.\n\nReproduced against `vikunja/vikunja:2.5.0`.\n\n## Recommended Fix\n\nMake `CanDelete` mirror `CanCreate`: after checking `CanUpdate` on the base task, also require `CanRead` on `OtherTaskID`.","severity":{"version":"4.0","score":2.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","level":"low","from":"ghsa"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-285","CWE-862"],"published":"2026-10-09T20:54:52Z","modified":"2026-10-19T21:41:23Z","fixes":[{"ecosystem":"Go","package":"code.vikunja.io/api","fixed_in":["2.6.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Go","package":"code.vikunja.io/api","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9"},{"last_affected":"2.5.0"},{"fixed":"2.6.0"}]}]},{"source":"osv","ecosystem":"Go","package":"code.vikunja.io/api","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9"},{"fixed":"2.6.0"}]}]},{"source":"osv","ecosystem":"Go","package":"code.vikunja.io/api","ranges":[{"type":"SEMVER","events":[{"introduced":"0"}]}]}],"references":[{"url":"https://github.com/go-vikunja/vikunja/security/advisories/GHSA-w2ch-4xgr-22ww","tags":["web","advisory"]},{"url":"https://github.com/go-vikunja/vikunja/pull/3688","tags":["web"]},{"url":"https://github.com/go-vikunja/vikunja/commit/077dc4de79ce6f1ab59215a2c7bf9b30423685f2","tags":["web"]},{"url":"https://github.com/go-vikunja/vikunja/releases/tag/v2.6.0","tags":["web"]},{"url":"https://github.com/advisories/GHSA-w2ch-4xgr-22ww"},{"url":"https://github.com/go-vikunja/vikunja","tags":["package"]}],"sources":{"ghsa":[{"id":"GHSA-w2ch-4xgr-22ww","title":"Vikunja: Task relation deletion does not check read access to the other task, allowing cross-project relation removal","description":"## Summary\n\nDeleting 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.\n\n## Details\n\n`TaskRelation.CanCreate` (`pkg/models/task_relation_permissions.go:32-52`) requires write access on `TaskID` **and** read access on `OtherTaskID`.\n\n`TaskRelation.CanDelete` (`pkg/models/task_relation_permissions.go:25-29`) only checks `Task{ID: rel.TaskID}.CanUpdate(s, a)`; `OtherTaskID` is never authorized.\n\n`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.\n\nAffects `DELETE /api/v1/tasks/{id}/relations/{kind}/{otherTaskId}` and the equivalent v2 endpoint.\n\n## Impact\n\nLow, 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.\n\nPreconditions: the attacker has write access to a project containing a task that is already related to a task in a project they cannot access.\n\n## Proof of Concept\n\n1. As `owner`, create project `P_near` with task `near` and project `P_far` with task `far`.\n2. Share `P_near` with `attacker` at write permission (`permission: 1`). Do not share `P_far`.\n3. As `owner`: `PUT /api/v1/tasks/{near}/relations` with `{\"other_task_id\": far, \"relation_kind\": \"related\"}` -\u003e 200.\n4. As `attacker`: `GET /api/v1/tasks/{far}` -\u003e 403 (confirms no access).\n5. As `attacker`: `PUT /api/v1/tasks/{near}/relations` with the same body -\u003e 403 (create path is enforced).\n6. As `attacker`: `DELETE /api/v1/tasks/{near}/relations/related/{far}` -\u003e 200 `\"Successfully deleted.\"`\n7. As `owner`: `GET /api/v1/tasks/{far}` -\u003e `related_tasks` is now empty.\n\nReproduced against `vikunja/vikunja:2.5.0`.\n\n## Recommended Fix\n\nMake `CanDelete` mirror `CanCreate`: after checking `CanUpdate` on the base task, also require `CanRead` on `OtherTaskID`.","status":"published","published":"2026-10-09T20:54:52Z","modified":"2026-10-09T20:54:52Z","cvss":[{"version":"4.0","score":2.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","level":"low"}],"cwes":["CWE-285","CWE-862"],"updated_at":"2026-11-02T14:41:31.175024Z"}],"osv":[{"id":"GHSA-w2ch-4xgr-22ww","title":"Vikunja: Task relation deletion does not check read access to the other task, allowing cross-project relation removal","description":"## Summary\n\nDeleting 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.\n\n## Details\n\n`TaskRelation.CanCreate` (`pkg/models/task_relation_permissions.go:32-52`) requires write access on `TaskID` **and** read access on `OtherTaskID`.\n\n`TaskRelation.CanDelete` (`pkg/models/task_relation_permissions.go:25-29`) only checks `Task{ID: rel.TaskID}.CanUpdate(s, a)`; `OtherTaskID` is never authorized.\n\n`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.\n\nAffects `DELETE /api/v1/tasks/{id}/relations/{kind}/{otherTaskId}` and the equivalent v2 endpoint.\n\n## Impact\n\nLow, 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.\n\nPreconditions: the attacker has write access to a project containing a task that is already related to a task in a project they cannot access.\n\n## Proof of Concept\n\n1. As `owner`, create project `P_near` with task `near` and project `P_far` with task `far`.\n2. Share `P_near` with `attacker` at write permission (`permission: 1`). Do not share `P_far`.\n3. As `owner`: `PUT /api/v1/tasks/{near}/relations` with `{\"other_task_id\": far, \"relation_kind\": \"related\"}` -\u003e 200.\n4. As `attacker`: `GET /api/v1/tasks/{far}` -\u003e 403 (confirms no access).\n5. As `attacker`: `PUT /api/v1/tasks/{near}/relations` with the same body -\u003e 403 (create path is enforced).\n6. As `attacker`: `DELETE /api/v1/tasks/{near}/relations/related/{far}` -\u003e 200 `\"Successfully deleted.\"`\n7. As `owner`: `GET /api/v1/tasks/{far}` -\u003e `related_tasks` is now empty.\n\nReproduced against `vikunja/vikunja:2.5.0`.\n\n## Recommended Fix\n\nMake `CanDelete` mirror `CanCreate`: after checking `CanUpdate` on the base task, also require `CanRead` on `OtherTaskID`.","status":"published","published":"2026-10-09T20:54:52Z","modified":"2026-10-19T21:41:23.229510718Z","cvss":[{"version":"4.0","score":2.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","level":"low"}],"cwes":["CWE-285","CWE-862"],"updated_at":"2026-11-02T15:46:58.04805Z"},{"id":"GO-2026-6754","title":"Vikunja: Task relation deletion does not check read access to the other task, allowing cross-project relation removal in code.vikunja.io/api","description":"Vikunja: Task relation deletion does not check read access to the other task, allowing cross-project relation removal in code.vikunja.io/api.\n\nNOTE: The source advisory for this report contains additional versions that could not be automatically mapped to standard Go module versions.\n\n(If this is causing false-positive reports from vulnerability scanners, please suggest an edit to the report.)\n\nThe additional affected modules and versions are: code.vikunja.io/api from v0.9.0 before v2.6.0.","status":"published","published":"2026-10-19T21:08:26Z","modified":"2026-10-19T21:31:37.903462238Z","updated_at":"2026-11-02T15:47:03.11707Z"}]}}
