CVE-2026-55064: Code.vikunja.io/api Write-only User Can Detach Shared Vulnerability

MODERATE
Advisory Identifier: GHSA-44v6-7fxq-vgf4
CVSS 4.3
CWE-862
Published
Advisory Summary

CVE-2026-55064 is a MODERATE (CVSS 4.3) write-only user can detach shared vulnerability (CWE-862: Missing Authorization) affecting Code.vikunja.io/api libraries in the Go ecosystem. The issue involves write-only user can detach shared project from parent hierarchy via parent_project_id=0. Remediation is available by upgrading affected packages to version 2.4.0.

CVE-2026-55064 at a Glance

CVE Identifier CVE-2026-55064
GHSA Identifier GHSA-44v6-7fxq-vgf4
Severity MODERATE
CVSS v3.1 Base Score 4.3
CWE Classification CWE-862: Missing Authorization
Affected Product Code.vikunja.io/api
Package Ecosystem Go
Published Date
Last Updated

Security Impact

CVSS: 4.3

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required Low (PR:L)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality None (C:N)
Integrity Low (I:L)
Availability None (A:N)

How to Fix CVE-2026-55064

To remediate this vulnerability, upgrade all affected Code.vikunja.io/api dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

code.vikunja.io/api fixed: 2.4.0
Upgrade code.vikunja.io/api to 2.4.0
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for code.vikunja.io/api

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-55064, inspect your project dependencies for Code.vikunja.io/api:

inspect dependencies for code.vikunja.io/api

Analyze the terminal output to locate any version of Code.vikunja.io/api in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 2.4.0), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-862: Missing Authorization, where the product does not perform an authorization check when an actor attempts to access a resource or perform an action. In Code.vikunja.io/api, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary The fix for CVE-2026-35595 (project re-parenting privilege escalation) only gates reparent operations when `parent_project_id > 0`. A user with Write (but not Admin) permission on a shared child project can detach it from its parent by sending `parent_project_id: 0`, bypassing the Admin requirement. This severs the recursive CTE permission inheritance chain, potentially disrupting the project hierarchy and affecting inherited access for other collaborators. ## Affected component - **Package:** go-vikunja/vikunja - **Affected versions:** v2.3.0 and later (including latest unstable v2.3.0-246-9852aff4). The fix for CVE-2026-35595 was introduced in v2.3.0 but left the detach-to-root case unpatched. Fixed in 2.4.0. - **Tested on:** Vikunja v2.3.0 (Docker image vikunja/vikunja:2.3.0) AND latest unstable (vikunja/vikunja:unstable, v2.3.0-246-9852aff4 built 2026-04-27) ## Technical detail ### Vulnerable code **File: `pkg/models/project.go` (lines 1009-1041)** ```go // GHSA-2vq4-854f-5c72 / CVE-2026-35595: the recursive permission CTE // cascades Admin from any owned ancestor, so moving a shared child // under an attacker-owned root grants Admin on the child. Require // Admin on both sides of a reparent. // // Only gate on non-zero ParentProjectID: the generic update handler // binds a fresh struct, so an omitted parent_project_id is // indistinguishable from an explicit 0. Detach-to-root is therefore // out of scope here -- a proper fix needs a pointer field. if project.ParentProjectID > 0 { // ... Admin check (lines 1019-1041) -- SKIPPED when ParentProjectID == 0 } ``` **File: `pkg/models/project_permissions.go` (line 145)** ```go if p.ParentProjectID != 0 && p.ParentProjectID != ol.ParentProjectID { // reparent permission check -- SKIPPED when ParentProjectID == 0 } ``` **File: `pkg/models/project.go` (line 1065)** ```go colsToUpdate := []string{ "title", "is_archived", "identifier", "hex_color", "parent_project_id", // <-- ALWAYS included, writes 0 to DB "position", } ``` ### Why it's exploitable 1. The generic web handler (`pkg/web/handler/update.go:37`) creates a fresh empty `Project{}` struct -- `ParentProjectID` defaults to Go's zero value (0). 2. When JSON body contains `"parent_project_id": 0`, the struct has `ParentProjectID == 0`. 3. `CanUpdate` at line 145: `ParentProjectID != 0` is false -- reparent check skipped -- falls through to `CanWrite` which succeeds (attacker has Write). 4. `UpdateProject` at line 1018: `ParentProjectID > 0` is false -- Admin gate skipped entirely. 5. xorm writes `parent_project_id = 0` because `"parent_project_id"` is always in `colsToUpdate` with `Cols()`. 6. The project is detached from its parent hierarchy. ### Precondition checklist - [x] Attacker has authenticated account - [x] Attacker has Write permission on a child project (via direct share or team membership) - [x] The target project has a non-zero parent_project_id (it's a child of another project) - [x] Default Vikunja configuration (no special setup needed) ### Reproduction **Prerequisites:** Two users (victim = project owner, attacker = Write-only collaborator), a parent project, and a child project shared with the attacker at Write permission. 1. Authenticate as attacker: ```bash TOKEN=$(curl -s -X POST http://localhost:3456/api/v1/login \ -H 'Content-Type: application/json' \ -d '{"username":"user_a","password":"UserAPassword1!"}' | jq -r '.token') ``` 2. Verify attacker does NOT have Admin (delete should return 403): ```bash curl -s -o /dev/null -w '%{http_code}' -X DELETE http://localhost:3456/api/v1/projects/4 \ -H "Authorization: Bearer $TOKEN" # Expected: 403 ``` 3. Exploit -- detach project from parent: ```bash curl -s -X POST http://localhost:3456/api/v1/projects/4 \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"title":"Sensitive Child Project","parent_project_id":0}' ``` 4. Verify detachment: ```bash curl -s http://localhost:3456/api/v1/projects/4 \ -H "Authorization: Bearer $TOKEN" | jq '.parent_project_id' # Returns: 0 (was: 3) ``` ### Evidence (3 independent runs) | Run | parent_project_id BEFORE | DELETE attempt (proves no Admin) | parent_project_id AFTER | Result | |-----|--------------------------|----------------------------------|-------------------------|--------| | 1 | 3 (child of project 3) | HTTP 403 Forbidden | null (detached to root) | CONFIRMED | | 2 | 3 (child of project 3) | HTTP 403 Forbidden | null (detached to root) | CONFIRMED | | 3 | 3 (child of project 3) | HTTP 403 Forbidden | null (detached to root) | CONFIRMED | *Note: "null (detached to root)" means `parent_project_id` was set to `0` in the database, making the project a root-level project with no parent.* ## Impact - **Unauthorized hierarchy modification:** A user with only Write permission can detach a child project from its parent, which should require Admin permission (as established by the CVE-2026-35595 fix for non-zero reparents). - **Permission inheritance disruption:** The recursive CTE permission model traverses `parent_project_id` upward. Detaching a project severs this chain, potentially causing other collaborators who inherited access through the parent to lose their permissions on the detached project. - **Organizational disruption:** The project moves from a structured hierarchy to a root-level project, breaking the owner's intended organizational structure. ## Suggested fix Use a pointer field `*int64` for `ParentProjectID` to distinguish between "field omitted" (nil) and "explicitly set to 0" (detach). The fix commit itself acknowledges this at `project.go:1017`: "a proper fix needs a pointer field." Alternatively, add a dedicated `detach` boolean field or a separate API endpoint for detaching projects, with its own Admin permission check.

CVE-2026-55064 Affected Code.vikunja.io/api Versions

1 component(s)
Fixed Versions:
2.4.0
Product / Package Ecosystem Affected Versions Fixed Version
code.vikunja.io/api
Go
≥ 2.3.0
< 2.4.0
2.4.0

References

Frequently Asked Questions

What is CVE-2026-55064?
CVE-2026-55064 is a security vulnerability in Code.vikunja.io/api involving Write-only user can detach shared project from parent hierarchy via parent_project_id=0 under CWE-862: Missing Authorization.
Which Code.vikunja.io/api versions are affected?
Affected versions include releases prior to 2.4.0 across affected packages: code.vikunja.io/api.
What version fixes CVE-2026-55064?
Upgrading to 2.4.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is code.vikunja.io/api affected?
Yes, code.vikunja.io/api is affected in releases prior to 2.4.0. Upgrade your dependency to 2.4.0 or later.
What is the CVSS score of CVE-2026-55064?
CVE-2026-55064 has a CVSS v3.1 base score of 4.3 (MODERATE) with vector string CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N.
What CWE is associated with CVE-2026-55064?
CVE-2026-55064 is classified under CWE-862: Missing Authorization (The product does not perform an authorization check when an actor attempts to access a resource or perform an action.).
How can I check Go dependencies for CVE-2026-55064?
Run the dependency tree command: 'inspect dependencies for code.vikunja.io/api'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-44v6-7fxq-vgf4",
  "modified" : "2026-08-28T16:39:08Z",
  "published" : "2026-08-28T16:39:08Z",
  "aliases" : [ "CVE-2026-55064" ],
  "summary" : "Vikunja has an incomplete fix for CVE-2026-35595: Write-only user can detach shared project from parent hierarchy via parent_project_id=0",
  "details" : "## Summary\n\nThe fix for CVE-2026-35595 (project re-parenting privilege escalation) only gates reparent operations when `parent_project_id > 0`. A user with Write (but not Admin) permission on a shared child project can detach it from its parent by sending `parent_project_id: 0`, bypassing the Admin requirement. This severs the recursive CTE permission inheritance chain, potentially disrupting the project hierarchy and affecting inherited access for other collaborators.\n\n## Affected component\n\n- **Package:** go-vikunja/vikunja\n- **Affected versions:** v2.3.0 and later (including latest unstable v2.3.0-246-9852aff4). The fix for CVE-2026-35595 was introduced in v2.3.0 but left the detach-to-root case unpatched. Fixed in 2.4.0.\n- **Tested on:** Vikunja v2.3.0 (Docker image vikunja/vikunja:2.3.0) AND latest unstable (vikunja/vikunja:unstable, v2.3.0-246-9852aff4 built 2026-04-27)\n\n## Technical detail\n\n### Vulnerable code\n\n**File: `pkg/models/project.go` (lines 1009-1041)**\n```go\n// GHSA-2vq4-854f-5c72 / CVE-2026-35595: the recursive permission CTE\n// cascades Admin from any owned ancestor, so moving a shared child\n// under an attacker-owned root grants Admin on the child. Require\n// Admin on both sides of a reparent.\n//\n// Only gate on non-zero ParentProjectID: the generic update handler\n// binds a fresh struct, so an omitted parent_project_id is\n// indistinguishable from an explicit 0. Detach-to-root is therefore\n// out of scope here -- a proper fix needs a pointer field.\nif project.ParentProjectID > 0 {\n    // ... Admin check (lines 1019-1041) -- SKIPPED when ParentProjectID == 0\n}\n```\n\n**File: `pkg/models/project_permissions.go` (line 145)**\n```go\nif p.ParentProjectID != 0 && p.ParentProjectID != ol.ParentProjectID {\n    // reparent permission check -- SKIPPED when ParentProjectID == 0\n}\n```\n\n**File: `pkg/models/project.go` (line 1065)**\n```go\ncolsToUpdate := []string{\n    \"title\", \"is_archived\", \"identifier\", \"hex_color\",\n    \"parent_project_id\",  // <-- ALWAYS included, writes 0 to DB\n    \"position\",\n}\n```\n\n### Why it's exploitable\n\n1. The generic web handler (`pkg/web/handler/update.go:37`) creates a fresh empty `Project{}` struct -- `ParentProjectID` defaults to Go's zero value (0).\n2. When JSON body contains `\"parent_project_id\": 0`, the struct has `ParentProjectID == 0`.\n3. `CanUpdate` at line 145: `ParentProjectID != 0` is false -- reparent check skipped -- falls through to `CanWrite` which succeeds (attacker has Write).\n4. `UpdateProject` at line 1018: `ParentProjectID > 0` is false -- Admin gate skipped entirely.\n5. xorm writes `parent_project_id = 0` because `\"parent_project_id\"` is always in `colsToUpdate` with `Cols()`.\n6. The project is detached from its parent hierarchy.\n\n### Precondition checklist\n\n- [x] Attacker has authenticated account\n- [x] Attacker has Write permission on a child project (via direct share or team membership)\n- [x] The target project has a non-zero parent_project_id (it's a child of another project)\n- [x] Default Vikunja configuration (no special setup needed)\n\n### Reproduction\n\n**Prerequisites:** Two users (victim = project owner, attacker = Write-only collaborator), a parent project, and a child project shared with the attacker at Write permission.\n\n1. Authenticate as attacker:\n```bash\nTOKEN=$(curl -s -X POST http://localhost:3456/api/v1/login \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"username\":\"user_a\",\"password\":\"UserAPassword1!\"}' | jq -r '.token')\n```\n\n2. Verify attacker does NOT have Admin (delete should return 403):\n```bash\ncurl -s -o /dev/null -w '%{http_code}' -X DELETE http://localhost:3456/api/v1/projects/4 \\\n  -H \"Authorization: Bearer $TOKEN\"\n# Expected: 403\n```\n\n3. Exploit -- detach project from parent:\n```bash\ncurl -s -X POST http://localhost:3456/api/v1/projects/4 \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"title\":\"Sensitive Child Project\",\"parent_project_id\":0}'\n```\n\n4. Verify detachment:\n```bash\ncurl -s http://localhost:3456/api/v1/projects/4 \\\n  -H \"Authorization: Bearer $TOKEN\" | jq '.parent_project_id'\n# Returns: 0 (was: 3)\n```\n\n### Evidence (3 independent runs)\n\n| Run | parent_project_id BEFORE | DELETE attempt (proves no Admin) | parent_project_id AFTER | Result |\n|-----|--------------------------|----------------------------------|-------------------------|--------|\n| 1 | 3 (child of project 3) | HTTP 403 Forbidden | null (detached to root) | CONFIRMED |\n| 2 | 3 (child of project 3) | HTTP 403 Forbidden | null (detached to root) | CONFIRMED |\n| 3 | 3 (child of project 3) | HTTP 403 Forbidden | null (detached to root) | CONFIRMED |\n\n*Note: \"null (detached to root)\" means `parent_project_id` was set to `0` in the database, making the project a root-level project with no parent.*\n\n## Impact\n\n- **Unauthorized hierarchy modification:** A user with only Write permission can detach a child project from its parent, which should require Admin permission (as established by the CVE-2026-35595 fix for non-zero reparents).\n- **Permission inheritance disruption:** The recursive CTE permission model traverses `parent_project_id` upward. Detaching a project severs this chain, potentially causing other collaborators who inherited access through the parent to lose their permissions on the detached project.\n- **Organizational disruption:** The project moves from a structured hierarchy to a root-level project, breaking the owner's intended organizational structure.\n\n## Suggested fix\n\nUse a pointer field `*int64` for `ParentProjectID` to distinguish between \"field omitted\" (nil) and \"explicitly set to 0\" (detach). The fix commit itself acknowledges this at `project.go:1017`: \"a proper fix needs a pointer field.\"\n\nAlternatively, add a dedicated `detach` boolean field or a separate API endpoint for detaching projects, with its own Admin permission check.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "code.vikunja.io/api"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "2.3.0"
      }, {
        "fixed" : "2.4.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/go-vikunja/vikunja/security/advisories/GHSA-44v6-7fxq-vgf4"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/go-vikunja/vikunja"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-862" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-08-28T16:39:08Z",
    "nvd_published_at" : null
  }
}