GHSA-hjx8-qv73-f7cm
## Summary A collaborator removed from a project keeps a **live, automatic feed** of that project's contents, because nothing on any revocation path deletes the webhook they created while they had access. Vikunja already has a revocation-cleanup routine that deletes other derived rows for exactly this reason. Its set is `{task_assignees, subscriptions}`. **`webhooks` and `link_shares` — the only two rows that carry a live channel into the project — are not in it**, and the routine is wired to one of four revocation paths. ## Details `pkg/models/teams.go:411` — `cleanupTaskMembersAfterTeamRemoval`, added in `9358954c9`, whose commit message states the invariant: *"cleanup team memberships, assignments and subscriptions when users lose access to a project"*. ```go canRead, _, permErr := project.CanRead(s, &user.User{ID: memberID}) ... if !canRead { projectsToCleanup = append(projectsToCleanup, projectID) } ... _, err = s.In("task_id", taskIDs).And("user_id = ?", memberID). Delete(&TaskAssginee{}) _, err = s.In("entity_id", taskIDs). Where("entity_type = ? AND user_id = ?", SubscriptionEntityTask, memberID). Delete(&Subscription{}) _, err = s.In("entity_id", projectsToCleanup). Where("entity_type = ? AND user_id = ?", SubscriptionEntityProject, memberID). Delete(&Subscription{}) ``` So you have already decided that losing read access must delete rows the departing user left behind, and that the test is a live `project.CanRead`. The gap is which rows are in the set. **And there is a second level, which is the sharper one.** That routine has exactly one caller: ``` pkg/models/listeners.go:1642 err = cleanupTaskMembersAfterTeamRemoval(s, event.Team.ID, event.Member.ID) ``` against four revocation paths: ``` pkg/models/project_team.go:151 TeamProject.Delete pkg/models/project_users.go:141 ProjectUser.Delete <- grep -c cleanup inside: 0 pkg/models/project_users.go:248 ProjectUser.Update <- the downgrade path pkg/model
Properties
- severity
- medium
- summary
- Vikunja: Webhooks and link shares survive every revocation path, so a removed collaborator keeps a live feed
- cvss_score
- 6.5
- retrieved_at
- 2026-10-10T02:17:04+00:00
- ghsa_published
- 2026-10-09T20:57:41Z
- source_url
- https://github.com/advisories/GHSA-hjx8-qv73-f7cm
- ghsa_updated
- 2026-10-09T20:57:42Z
- ghsa_id
- GHSA-hjx8-qv73-f7cm
- last_source
- GitHub Advisory Database
- cve_id
- GHSA-hjx8-qv73-f7cm
- cvss_vector
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
- signal_observed_at
- 2026-10-10T02:17:04+00:00
- is_ghsa_only
- true
Related Entities (5)
VULNERABLE_TO (1)
AFFECTS (1)
HAS_WEAKNESS (2)
REPORTED_BY (1)
Explore deeper with Ninja Signal's threat intelligence graph