| CVE | Vulnerability name | Date | Responsible Security Disclosure by | Vulnerabilities |
|---|---|---|---|---|
|
GHSA-pqr4-rxgp-hv2m
|
CommentBleed |
2026-08-09 |
Alpastx and xet7![]() Coordinated disclosure via GitHub Security Advisory GHSA-pqr4-rxgp-hv2m. |
|
Deleting a comment in WeKan is author-or-board-admin, and a board that switches on
restrictCommentEditing takes even the admin's ability away, leaving only the
author. That policy is real, it is tested, and over DDP it works.
It was enforced in a CardComments collection hook, which decides from the
Meteor userId of the invocation. Genuine server-internal work — copying a board,
cleanup, migrations — runs with no authenticated user, so the hook began:
// models/cardComments.js
if (!userId) {
return; // server-internal operation: not a user action, do not block it
}
An HTTP request is not a server-internal operation, but it does not carry a Meteor userId into the invocation context either. The REST handler had only the board-level check:
// server/models/cardComments.js — before
await Authentication.checkBoardAccess(req.userId, paramBoardId);
await CardComments.removeAsync({ _id: paramCommentId, cardId: paramCardId, boardId: paramBoardId });
checkBoardAccess answers "is this person a member of this board" — a
perfectly good question, and the wrong one for "may this person delete THIS comment". The
removal then reached the collection with no userId, the hook took its trusted path, and the
comment was gone. HTTP 200, somebody else's comment deleted, on a board with
restrictCommentEditing switched on, by a member who over DDP would have been
refused.
Both users only need to be ordinary members of the same board, and the API token of a normal account is enough. On a shared board that is harassment, and the destruction of a record somebody may be relying on.
The repair is not to teach the hook about HTTP callers — a hook cannot see an identity that was never put in the context. It is for the handler, which DOES know who is calling, to apply the rule itself:
restrictCommentEditing rule, with
req.userId, before removing anything;
That rule is now an exported function (assertCanMutateComment) which the
collection hooks and the REST handler both call, so DDP and HTTP cannot enforce different
things — which is the actual bug, in one sentence. The pure decision underneath it
(canEditComment) is unchanged, and so is DDP behaviour.
The if (!userId) path stays, because the internal callers it was written for are
real and would break without it. What changed is that a REST request no longer arrives there
anonymously.
tests/restCommentDeleteAcl.test.cjs pins that the handler loads the comment,
that a missing one is a 404, that the ACL runs BEFORE the removal, that board membership
alone is no longer sufficient, that the hooks still call the same function, and — so a
future comment-edit route cannot repeat this — that no REST PUT for
comments exists without the same check.
Reported by Alpastx via GitHub Security
Advisory
GHSA-pqr4-rxgp-hv2m,
confirmed against v10.73.0 with WITH_API=true. Fixed at the
upcoming WeKan release.