Vikunja: Cross-project task disclosure through subtask expansion
### Summary Vikunja lets a task in one project have a subtask that lives in a different project. When listing a project's tasks, an option pulls in those subtasks (?expand%5B%5D=subtasks). The main listing is correctly filtered to the projects the caller can access, but the subtask expansion is not: it returns the linked subtasks regardless of whether the caller has any access to the project they belong to. So a member with read-only access to one project can read the full content of tasks in other, private projects, any task that is a (recursive) subtask of a task the member is allowed to see. ### Details Subtasks can cross project boundaries; a task in an accessible project can be linked as the parent of a subtask that lives in a private project. Creating that link requires access to both tasks, so the link itself is set up legitimately by someone who has it. The problem is what happens afterwards, on read. When a task list is requested with subtask expansion, the server takes the tasks the caller is allowed to see and walks their subtask relations recursively to fetch the linked tasks. That fetch doesn't apply the access-control filter the main listing use, it returns every linked task by ID, with no check on whether the caller can access the project it belongs to. Because the walk is recursive, it also returns the entire subtree beneath each linked task. The effect is a cross-project read. A read-only member of project P who has no access at all to a private project Q can
- Products
- code.vikunja.io/api
- Affected versions
- Not specified
- Fixed versions
- 2.6.0
- CVEs
- Not specified
- Confirmed fixed CVEs
- Not specified
- Updated in this feed
- 31 Oct 2026, 08:45 UTC · Revision 1
Source evidence
Vikunja: Cross-project task disclosure through subtask expansion
core-osv · Original sourcePublished 9 Oct 2026, 20:54 UTC
### Summary Vikunja lets a task in one project have a subtask that lives in a different project. When listing a project's tasks, an option pulls in those subtasks (?expand%5B%5D=subtasks). The main listing is correctly filtered to the projects the caller can access, but the subtask expansion is not: it returns the linked subtasks regardless of whether the caller has any access to the project they belong to. So a member with read-only access to one project can read the full content of tasks in other, private projects, any task that is a (recursive) subtask of a task the member is allowed to see. ### Details Subtasks can cross project boundaries; a task in an accessible project can be linked as the parent of a subtask that lives in a private project. Creating that link requires access to both tasks, so the link itself is set up legitimately by someone who has it. The problem is what happens afterwards, on read. When a task list is requested with subtask expansion, the server takes the tasks the caller is allowed to see and walks their subtask relations recursively to fetch the linked tasks. That fetch doesn't apply the access-control filter the main listing use, it returns every linked task by ID, with no check on whether the caller can access the project it belongs to. Because the walk is recursive, it also returns the entire subtree beneath each linked task. The effect is a cross-project read. A read-only member of project P who has no access at all to a private project Q can
osv:GHSA-3hc7-r24j-rpwc
code.vikunja.io/api
Fixed versions: 2.6.0
Complete source record
{
"id": "9fe815951c94aff3f119436823ff7bac66a52d1c1c69eba50ce07280cf3aa1d9",
"url": "https://osv.dev/vulnerability/GHSA-3hc7-r24j-rpwc",
"method": "structured",
"payload": {
"kind": "advisory",
"title": "Vikunja: Cross-project task disclosure through subtask expansion",
"summary": "### Summary Vikunja lets a task in one project have a subtask that lives in a different project. When listing a project's tasks, an option pulls in those subtasks (?expand%5B%5D=subtasks). The main listing is correctly filtered to the projects the caller can access, but the subtask expansion is not: it returns the linked subtasks regardless of whether the caller has any access to the project they belong to. So a member with read-only access to one project can read the full content of tasks in other, private projects, any task that is a (recursive) subtask of a task the member is allowed to see. ### Details Subtasks can cross project boundaries; a task in an accessible project can be linked as the parent of a subtask that lives in a private project. Creating that link requires access to both tasks, so the link itself is set up legitimately by someone who has it. The problem is what happens afterwards, on read. When a task list is requested with subtask expansion, the server takes the tasks the caller is allowed to see and walks their subtask relations recursively to fetch the linked tasks. That fetch doesn't apply the access-control filter the main listing use, it returns every linked task by ID, with no check on whether the caller can access the project it belongs to. Because the walk is recursive, it also returns the entire subtree beneath each linked task. The effect is a cross-project read. A read-only member of project P who has no access at all to a private project Q can",
"evidence": "osv:GHSA-3hc7-r24j-rpwc",
"products": [
{
"name": {
"value": "code.vikunja.io/api",
"evidence": "/affected/0/package/name"
},
"prerequisites": [],
"fixed_versions": [
{
"value": "2.6.0",
"evidence": "/affected/0/ranges"
}
],
"affected_versions": []
}
],
"severity": "medium",
"structured": {
"id": "GHSA-3hc7-r24j-rpwc",
"aliases": [
"GHSA-3hc7-r24j-rpwc",
"GO-2026-6714"
],
"metrics": [
{
"type": "CVSS_V4",
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N"
}
],
"affected": [
{
"ranges": [
{
"type": "SEMVER",
"events": [
{
"introduced": "0"
},
{
"fixed": "2.6.0"
}
]
}
],
"package": {
"name": "code.vikunja.io/api",
"purl": "pkg:golang/code.vikunja.io/api",
"ecosystem": "Go"
},
"database_specific": {
"source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-3hc7-r24j-rpwc/GHSA-3hc7-r24j-rpwc.json",
"last_known_affected_version_range": "<= 2.5.0"
}
}
],
"modified": "2026-10-19T21:41:23.629997290Z",
"provider": "osv",
"references": [
"https://github.com/go-vikunja/vikunja/security/advisories/GHSA-3hc7-r24j-rpwc",
"https://github.com/go-vikunja/vikunja/pull/3688",
"https://github.com/go-vikunja/vikunja/commit/077dc4de79ce6f1ab59215a2c7bf9b30423685f2",
"https://github.com/go-vikunja/vikunja",
"https://github.com/go-vikunja/vikunja/releases/tag/v2.6.0"
],
"provider_metadata": {
"cwe_ids": [
"CWE-200",
"CWE-862"
],
"severity": "MODERATE",
"github_reviewed": true,
"nvd_published_at": null,
"github_reviewed_at": "2026-10-09T20:54:46Z"
}
},
"related_cves": [],
"security_relevant": true,
"severity_evidence": "/severity/0/score"
},
"source_id": "core-osv",
"document_id": 376811,
"published_at": "2026-10-09T20:54:46+00:00"
}