Vikunja: Saved filter creation with an empty filter string recalculates task positions across all tenants
### Summary In Vikunja v2.6.0 the task position recalculation that runs when a saved filter view is created fails open when the requesting user has no accessible projects. Instead of aborting, the search that feeds the recalculation loses its project scope entirely and returns every task in the instance. The server then writes one `task_positions` row per task per view (four views per filter) into views owned by the requesting user, including rows that reference tasks of other tenants the user can never see. Any authenticated user can trigger this. The impact is a cross-tenant integrity violation, a single-request write amplification primitive that scales with instance size, and a violation of the invariant stated in the fix for GHSA-w39f-h553-h2mx that position writes are scoped by project read access. ### Details Vikunja computes kanban and list ordering in a shared `task_positions` table keyed by `(task_id, project_view_id)`. When a saved filter is created, `SavedFilter.Create` (`pkg/models/saved_filters.go:130`) calls `CreateDefaultViewsForProject` (`pkg/models/project_view.go:816`) which, for every default view it creates, calls `RecalculateTaskPositions` with `addExistingTasksToView = true` (`pkg/models/project_view.go:447`). `RecalculateTaskPositions` (`pkg/models/task_position.go:312`) builds a `TaskCollection` and, for filter views (`view.ProjectID < -1`), resets the project scope to zero and injects the saved filter's filter string. The scope of the subsequent searc
- Products
- code.vikunja.io/api
- Affected versions
- Not specified
- Fixed versions
- 2.7.0
- CVEs
- CVE-2026-111918
- Confirmed fixed CVEs
- Not specified
- Updated in this feed
- 1 Nov 2026, 18:34 UTC · Revision 1
Source evidence
Vikunja: Saved filter creation with an empty filter string recalculates task positions across all tenants
core-osv · Original sourcePublished 9 Oct 2026, 20:57 UTC
### Summary In Vikunja v2.6.0 the task position recalculation that runs when a saved filter view is created fails open when the requesting user has no accessible projects. Instead of aborting, the search that feeds the recalculation loses its project scope entirely and returns every task in the instance. The server then writes one `task_positions` row per task per view (four views per filter) into views owned by the requesting user, including rows that reference tasks of other tenants the user can never see. Any authenticated user can trigger this. The impact is a cross-tenant integrity violation, a single-request write amplification primitive that scales with instance size, and a violation of the invariant stated in the fix for GHSA-w39f-h553-h2mx that position writes are scoped by project read access. ### Details Vikunja computes kanban and list ordering in a shared `task_positions` table keyed by `(task_id, project_view_id)`. When a saved filter is created, `SavedFilter.Create` (`pkg/models/saved_filters.go:130`) calls `CreateDefaultViewsForProject` (`pkg/models/project_view.go:816`) which, for every default view it creates, calls `RecalculateTaskPositions` with `addExistingTasksToView = true` (`pkg/models/project_view.go:447`). `RecalculateTaskPositions` (`pkg/models/task_position.go:312`) builds a `TaskCollection` and, for filter views (`view.ProjectID < -1`), resets the project scope to zero and injects the saved filter's filter string. The scope of the subsequent searc
osv:GHSA-fprf-r6rv-xg99
code.vikunja.io/api
Fixed versions: 2.7.0
Complete source record
{
"id": "518ed4c51f19d931de38cae7754f8e4ceab0f33dfab5ceb75da380ddcf54656b",
"url": "https://osv.dev/vulnerability/GHSA-fprf-r6rv-xg99",
"method": "structured",
"payload": {
"kind": "advisory",
"title": "Vikunja: Saved filter creation with an empty filter string recalculates task positions across all tenants",
"summary": "### Summary In Vikunja v2.6.0 the task position recalculation that runs when a saved filter view is created fails open when the requesting user has no accessible projects. Instead of aborting, the search that feeds the recalculation loses its project scope entirely and returns every task in the instance. The server then writes one `task_positions` row per task per view (four views per filter) into views owned by the requesting user, including rows that reference tasks of other tenants the user can never see. Any authenticated user can trigger this. The impact is a cross-tenant integrity violation, a single-request write amplification primitive that scales with instance size, and a violation of the invariant stated in the fix for GHSA-w39f-h553-h2mx that position writes are scoped by project read access. ### Details Vikunja computes kanban and list ordering in a shared `task_positions` table keyed by `(task_id, project_view_id)`. When a saved filter is created, `SavedFilter.Create` (`pkg/models/saved_filters.go:130`) calls `CreateDefaultViewsForProject` (`pkg/models/project_view.go:816`) which, for every default view it creates, calls `RecalculateTaskPositions` with `addExistingTasksToView = true` (`pkg/models/project_view.go:447`). `RecalculateTaskPositions` (`pkg/models/task_position.go:312`) builds a `TaskCollection` and, for filter views (`view.ProjectID < -1`), resets the project scope to zero and injects the saved filter's filter string. The scope of the subsequent searc",
"evidence": "osv:GHSA-fprf-r6rv-xg99",
"products": [
{
"name": {
"value": "code.vikunja.io/api",
"evidence": "/affected/0/package/name"
},
"prerequisites": [],
"fixed_versions": [
{
"value": "2.7.0",
"evidence": "/affected/0/ranges"
}
],
"affected_versions": []
}
],
"severity": "medium",
"structured": {
"id": "GHSA-fprf-r6rv-xg99",
"aliases": [
"CVE-2026-111918",
"GHSA-fprf-r6rv-xg99",
"GO-2026-6731"
],
"metrics": [
{
"type": "CVSS_V3",
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L"
}
],
"affected": [
{
"ranges": [
{
"type": "SEMVER",
"events": [
{
"introduced": "1.0.0-rc0"
},
{
"fixed": "2.7.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-fprf-r6rv-xg99/GHSA-fprf-r6rv-xg99.json",
"last_known_affected_version_range": "<= 2.6.0"
}
}
],
"modified": "2026-10-27T15:30:03.535306288Z",
"provider": "osv",
"references": [
"https://github.com/go-vikunja/vikunja/security/advisories/GHSA-fprf-r6rv-xg99",
"https://github.com/go-vikunja/vikunja/commit/24e4563a547be182b338be2f614df339a64a6058",
"https://github.com/go-vikunja/vikunja",
"https://github.com/go-vikunja/vikunja/releases/tag/v2.7.0"
],
"provider_metadata": {
"cwe_ids": [
"CWE-863"
],
"severity": "MODERATE",
"github_reviewed": true,
"nvd_published_at": null,
"github_reviewed_at": "2026-10-09T20:57:38Z"
}
},
"related_cves": [
{
"value": "CVE-2026-111918",
"evidence": "/aliases"
}
],
"security_relevant": true,
"severity_evidence": "/severity/0/score"
},
"source_id": "core-osv",
"document_id": 462570,
"published_at": "2026-10-09T20:57:38+00:00"
}