CVE-2026-111919
Vikunja 0.22.0 before 2.7.0 Improper Access Revocation via Webhooks
Vikunja 0.22.0 before 2.7.0 contains an improper access revocation vulnerability that allows removed collaborators to keep receiving project data through webhooks they created. Former collaborators keep receiving task titles and descriptions through their webhook, including content created after their access was revoked.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
7.1
Vikunja 0.22.0 before 2.7.0 Improper Access Revocation via Webhooks
Vikunja 0.22.0 before 2.7.0 contains an improper access revocation vulnerability that allows removed collaborators to keep receiving project data through webhooks they created. Former collaborators keep receiving task titles and descriptions through their webhook, including content created after their access was revoked.
- Record
CVE-2026-111919- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N - CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-613
- Updated
- 2026-11-02
EUVDEUVD-2026-103287
7.1
Vikunja 0.22.0 before 2.7.0 contains an improper access revocation vulnerability that allows removed collaborators to keep receiving project data through webhooks they created. Former collaborators keep receiving task titles and descriptions through their webhook, including content created after their access was revoked.
- Record
EUVD-2026-103287- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-276j-w3vx-r5jh
7.1
Vikunja 0.22.0 before 2.7.0 contains an improper access revocation vulnerability that allows...
Vikunja 0.22.0 before 2.7.0 contains an improper access revocation vulnerability that allows removed collaborators to keep receiving project data through webhooks they created. Former collaborators keep receiving task titles and descriptions through their webhook, including content created after their access was revoked.
- Record
GHSA-276j-w3vx-r5jh- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-613
- Updated
- 2026-11-02
GitHub AdvisoriesGHSA-hjx8-qv73-f7cm
6.5
Vikunja: Webhooks and link shares survive every revocation path, so a removed collaborator keeps a live feed
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".
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/models/team_members.go:91 TeamMember.Delete <- the only one wired up
Removing a user directly from a project — the ordinary operation — dispatches nothing at all.
PoC
Released vikunja/vikunja:2.6.0 Docker image.
The harness was proved first by reproducing a known-fixed advisory: GHSA-qfwc refuses the read-only member and returns the hash to the owner. Every run carries its own negative control.
The strongest form is on the team-removal path, where your cleanup routine does fire:
== OWNER removes collab from the TEAM ==
-- did the cleanup routine run? --
assignees now: [] <- POSITIVE CONTROL: it ran
collab direct read of task: HTTP 403 <- NEGATIVE CONTROL: access is gone
-- webhook still delivering? --
WEBHOOK RECEIVED task_title='post-team-removal secret'
task_desc='cleanup ran, webhook did not'
-- link share still redeemable? --
[{"title":"post-team-removal secret","description":"cleanup ran, webhook did not"}]
The two controls are the argument: your own routine executed and removed the assignee rows, and the collaborator's direct read is refused with 403 — and the webhook created before removal still delivers the contents of a task created after it.
On the ProjectUser.Delete path the same thing happens with no cleanup running at all.
One lab accommodation, stated plainly: non-routable outbound IPs were enabled so a loopback sink was reachable. In a default deployment an attacker simply uses a public URL, so this changes nothing about reachability.
Impact
A former collaborator receives task titles and descriptions for the whole project, continuously, after their access has been revoked — including content created after revocation.
The honest counter-argument, which we would rather state than have you find. Both artefacts stay visible and auditable to the owner afterwards: GET /projects/{p}/webhooks still shows…
- Record
GHSA-hjx8-qv73-f7cm- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-613, CWE-863
- Updated
- 2026-11-02
NVD
7.1
Vikunja 0.22.0 before 2.7.0 contains an improper access revocation vulnerability that allows removed collaborators to keep receiving project data through webhooks they created. Former collaborators keep receiving task titles and descriptions through their webhook, including content created after their access was revoked.
- Record
CVE-2026-111919- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-613
- Updated
- 2026-11-02
OSV
7.1
Vikunja 0.22.0 before 2.7.0 Improper Access Revocation via Webhooks
Vikunja 0.22.0 before 2.7.0 contains an improper access revocation vulnerability that allows removed collaborators to keep receiving project data through webhooks they created. Former collaborators keep receiving task titles and descriptions through their webhook, including content created after their access was revoked.
- Record
CVE-2026-111919- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-613
- Updated
- 2026-11-02
OSVGHSA-hjx8-qv73-f7cm
6.5
Vikunja: Webhooks and link shares survive every revocation path, so a removed collaborator keeps a live feed
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".
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/models/team_members.go:91 TeamMember.Delete <- the only one wired up
Removing a user directly from a project — the ordinary operation — dispatches nothing at all.
PoC
Released vikunja/vikunja:2.6.0 Docker image.
The harness was proved first by reproducing a known-fixed advisory: GHSA-qfwc refuses the read-only member and returns the hash to the owner. Every run carries its own negative control.
The strongest form is on the team-removal path, where your cleanup routine does fire:
== OWNER removes collab from the TEAM ==
-- did the cleanup routine run? --
assignees now: [] <- POSITIVE CONTROL: it ran
collab direct read of task: HTTP 403 <- NEGATIVE CONTROL: access is gone
-- webhook still delivering? --
WEBHOOK RECEIVED task_title='post-team-removal secret'
task_desc='cleanup ran, webhook did not'
-- link share still redeemable? --
[{"title":"post-team-removal secret","description":"cleanup ran, webhook did not"}]
The two controls are the argument: your own routine executed and removed the assignee rows, and the collaborator's direct read is refused with 403 — and the webhook created before removal still delivers the contents of a task created after it.
On the ProjectUser.Delete path the same thing happens with no cleanup running at all.
One lab accommodation, stated plainly: non-routable outbound IPs were enabled so a loopback sink was reachable. In a default deployment an attacker simply uses a public URL, so this changes nothing about reachability.
Impact
A former collaborator receives task titles and descriptions for the whole project, continuously, after their access has been revoked — including content created after revocation.
The honest counter-argument, which we would rather state than have you find. Both artefacts stay visible and auditable to the owner afterwards: GET /projects/{p}/webhooks still shows…
- Record
GHSA-hjx8-qv73-f7cm- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-613, CWE-863
- Updated
- 2026-11-02
OSVGO-2026-6733 No score
Vikunja: Webhooks and link shares survive every revocation path, so a removed collaborator keeps a live feed in code.vikunja.io/api
Vikunja: Webhooks and link shares survive every revocation path, so a removed collaborator keeps a live feed in code.vikunja.io/api
- Record
GO-2026-6733- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
code.vikunja.io/apiGo |
0.22.0 to 2.6.0 | None known |
code.vikunja.io/apiGo |
0.22.0 and later | None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-111919 is a high vulnerability (CVSS 7.1) in Go-vikunja Vikunja, published October 9, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.36%, the 29th percentile. No source names a fixed version.
References
github.com/go-vikunja/vikunja/security/advisories/GHSA-hjx8-qv73-f7cm
vulncheck.com/advisories/vikunja-0.22.0-before-2.7.0-improper-access-revocation-via-webhooks
nvd.nist.gov/vuln/detail/CVE-2026-111919
github.com/advisories/GHSA-276j-w3vx-r5jh
github.com/advisories/GHSA-hjx8-qv73-f7cm
github.com/CVEProject/cvelistV5/tree/main/cves/2026/111xxx/CVE-2026-111919.json
github.com/go-vikunja/vikunja
Left out here: the rest of 2 source descriptions. The JSON record has them.