Hall of Fame image from https://openclipart.org/detail/120343/trophy
Back to Hall of Fame Contents Back to Wekan Website

Contents / ChecklistBleed

CVE Vulnerability name Date Responsible Security Disclosure by Vulnerabilities
GHSA-gv8h-5p3p-6hx7

ChecklistBleed

2026-06-20 DavidCarliez (coordinated disclosure) and Claude (fix)

Did send detailed report with PoC!
  • 1. ChecklistBleed — cross-board checklist write via collection allow rule
  • CVSS:3.1 Moderate (AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N), CWE-863
  • Affected Wekan v9.62 and earlier
  • Fixed at upcoming WeKan release


Details

1. ChecklistBleed — any authenticated user can write checklist/checklist-item data into a private board they are not a member of (CWE-863)

WeKan boards are membership-scoped: a private board is only readable/writable by its active members. Checklists and checklist items are attached to a card and carry a denormalized boardId (so the board publication can filter them with a single reactive cursor). They are moved between cards by setting a new cardId (and, for items, a new checklistId) in an update; a server Checklists.before.update hook then re-derives boardId from the destination card.

The DDP collection write policies for Checklists and ChecklistItems authorize an update by checking only the current (pre-update) cardId of the document and asking whether the caller has write access to that card's board. They never validate the new destination (cardId, checklistId or boardId) supplied in the update modifier.

// server/permissions/checklists.js
Checklists.allow({
  async update(userId, doc) {
    return await allowIsBoardMemberWithWriteAccessByCard(userId, await Cards.findOneAsync(doc.cardId));
  },                                                 // <-- doc.cardId = SOURCE card
  fetch: ['userId', 'cardId'],
});
// server/permissions/checklistItems.js has the same pattern.
    

Because every logged-in user can create their own board (where they are a write-capable member), an attacker can create a checklist or checklist item on their own card and then, in a single /checklists/update or /checklistItems/update DDP call, $set its cardId to a card in a victim's private board. The allow rule sees the attacker's own source card, approves the write, and the Checklists.before.update hook denormalizes boardId from the destination card — so the attacker-controlled checklist/item becomes attached to the victim's private board's publication/query model. The only practical precondition is knowledge of a target private card id; board membership is not required.

The protected moveChecklist Meteor method (server/models/checklists.js) correctly checks board membership on both the source and the destination card:

if (!(await allowIsBoardMemberByCard(this.userId, sourceCard))) {
  throw new Meteor.Error('not-authorized', 'Not authorized to move checklist from source card');
}
if (!(await allowIsBoardMemberByCard(this.userId, newCard))) {
  throw new Meteor.Error('not-authorized', 'Not authorized to move checklist to target card');
}
    

But a DDP client can simply bypass that method and update the collections directly over the Meteor websocket, where only the (source-only) allow rules applied.

Impact: a low-privileged authenticated user could inject attacker-controlled checklist and checklist-item data (titles, completion state) into any private board by target card id, defeating board-level access control. This is the same class as BoardBleed (GHSA-gm7v-pc38-53jr), which had only fixed the boardId-based cross-board move for Cards/Lists/Swimlanes; Checklists and ChecklistItems move via cardId/checklistId and were not covered.

2. The moveChecklist Meteor method and REST API were not affected

The server-side moveChecklist Meteor method validates both source and destination board membership, and server-side trusted code bypasses allow/deny entirely. Only the DDP allow/deny layer, reachable directly over the Meteor websocket by any authenticated client, was vulnerable.

Fix

Two helpers, denyCrossBoardMoveByCard(userId, modifier) and denyCrossBoardMoveByChecklistItem(userId, modifier), were added in server/lib/utils.js, and a deny update rule was added to each of Checklists and ChecklistItems (server/permissions/checklists.js, server/permissions/checklistItems.js). The deny rule inspects the update modifier and rejects the move unless the caller has write access (active, write-capable member) on the destination board — resolved from the new boardId, the new cardId's card, or (for items) the new checklistId's checklist → card. An unknown/missing destination fails closed. A cross-board checklist move is therefore now only permitted into a board where the user is genuinely a write-capable member, while same-card edits and legitimate moves between boards the user belongs to keep working. A regression test (server/lib/tests/checklistbleed.security.tests.js) was added.



Timeline Details
2026-06-20 Report received from DavidCarliez (coordinated disclosure, GHSA-gv8h-5p3p-6hx7).
Upcoming release Fixed at the upcoming WeKan release, by denying any DDP update that moves a Checklist/ChecklistItem to a destination board the caller has no write access to.


Back to Hall of Fame Contents Back to Wekan Website