Skip to content
Heist/ feed
Back to feed
MediumFix available

Vikunja: Saved filter creation with an empty filter string recalculates task positions across all tenants

Type: advisorySources: OSV

### 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 source

Published 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"
}