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

Contents / ExcelBleed

CVE Vulnerability name Date Responsible Security Disclosure by Vulnerabilities
GHSA-mwq8-ccpm-r533

ExcelBleed

2026-07-05 sec-reex (coordinated disclosure) and xet7 (fix)

Did send detailed report with runtime-confirmed PoC!
  • 1. ExcelBleed — un-awaited async access-control guard in the Excel-export REST route lets any authenticated user export any private board
  • CVSS:3.1 Moderate (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N), CWE-862, CWE-639
  • Affected Wekan v9.57.0 and earlier
  • Fixed at upcoming WeKan release


Details

1. ExcelBleed — any authenticated non-member can export the full contents of any private board (CWE-862, CWE-639)

WeKan boards are membership-scoped: a private board is only readable by its active members. The REST export routes gate access with an async canExport(user) guard that resolves the board and returns board.isVisibleBy(user). Every export route (models/export.js, exportPDF.js, exportExcelCard.js, import.js) awaits that guard — except the Excel-export route.

In models/exportExcel.js the guard was called without await. Because canExport is async, it returns a Promise, and a Promise object is always truthy — so the || short-circuit approved the export unconditionally and the real visibility result (which never resolved before the branch was taken) was ignored:

// models/exportExcel.js  (vulnerable)
const exporterExcel = new ExporterExcel(boardId, userLanguage);
if (exporterExcel.canExport(user) || impersonateDone) {   // <-- missing await; Promise is always truthy
  ...
  exporterExcel.build(res);                                // <-- runs for ANY authenticated user
} else {
  res.end(TAPi18n.__('user-can-not-export-excel'));
}

// models/server/ExporterExcel.js
async canExport(user) {
  const board = await ReactiveCache.getBoard(this._boardId);
  return board && board.isVisibleBy(user);                 // correct — but never awaited above
}
    

Steps to reproduce (runtime-confirmed on v9.57.0). As non-member bob against a private board owned by alice:

Impact: an authenticated non-member could read the full contents of any board by id — card titles and descriptions, lists, swimlanes, members and metadata — including private boards they are not a member of. This is the same un-awaited async-auth bug class WeKan already remediated in BFLABleed (48 REST endpoints, v9.22), CloneBleed (un-awaited allowIsBoardMemberByCard, v9.35) and TokenBleed. The Excel-export route was the lone remaining un-awaited canExport call site.

Fix

The guard is now awaited, matching every other export route:

// models/exportExcel.js  (fixed)
if ((await exporterExcel.canExport(user)) || impersonateDone) {
  ...
  exporterExcel.build(res);
} else {
  res.end(TAPi18n.__('user-can-not-export-excel'));
}
    

A non-member now correctly receives the "cannot export" response for /api/boards/:boardId/exportExcel, identical to the already-hardened /export route.



Timeline Details
2026-07-05 Report received from sec-reex (coordinated disclosure, GHSA-mwq8-ccpm-r533), with a read-only, runtime-confirmed PoC as part of an incomplete-patch measurement study.
Upcoming release Fixed at the upcoming WeKan release, by adding await to the canExport guard in models/exportExcel.js so the Excel-export route enforces board visibility like every other export route.


Back to Hall of Fame Contents Back to Wekan Website