Wekan
Open source kanban board application built with Meteor
Alternative to: trello
v10.03
2026-07-19v10.03 2026-07-19 WeKan ® release
This release fixes the following CRITICAL VULNERABILITIES, all reported by meifukun: RedirectBleed, SourceBleed, LiveBleed, CasBleed, MetricsBleed, ImpersonateBleed and InviteBleed:
- Fix RedirectBleed: avatar localization validated only the original host and then followed redirects with native fetch, so a public avatar URL could 302 to a loopback/private/metadata address (SSRF). Avatars are now fetched via the DNS-pinned, redirect-rejecting fetchSafe, and profile.avatarUrl is scheme-validated (http/https/data:image/local) at write time. Thanks to meifukun and xet7.
- Fix SourceBleed: a Trello board imported with a javascript: url was stored as an activity source.url and rendered as a clickable activity-sidebar link, so a board admin clicking it ran attacker JavaScript (stored XSS, Meteor.loginToken theft). Only http(s) source URLs are now stored and linked. Thanks to meifukun and xet7.
- Fix LiveBleed: the live Trello import fetched attacker-controlled attachment/background/avatar URLs with bare fetch (no SSRF guard) and stored the response for read-back through the attachment API (non-blind SSRF). Every live-import download is now gated by validateAttachmentUrl, matching the offline import path. Thanks to meifukun and xet7.
- Fix CasBleed: CAS login stored validated user data in a module-global (_userData) shared across concurrent logins, so two logins could race and issue an attacker a session for a victim’s account. The user data is now bound per credential token. Thanks to meifukun and xet7.
- Fix MetricsBleed: the /metrics endpoint trusted a client-supplied X-Forwarded-For header unconditionally, so anyone could forge a whitelisted IP and read operational metrics. XFF is now trusted only when METRICS_TRUST_PROXY is set (parsed spoof-resistantly from the right); otherwise the real socket address is used. Thanks to meifukun and xet7.
- Fix ImpersonateBleed: several board export endpoints treated any historical ImpersonatedUsers record as an authorization bypass (canExport || impersonateDone), so a demoted former admin could export private boards forever. The impersonation bypass is removed; export requires real board visibility (canExport) only. Thanks to meifukun and xet7.
- Fix InviteBleed: invitation registration used a 6-digit Math.random() code (~900k keyspace) with no throttling, brute-forceable to hijack an invited account and its private boards. Codes are now a 128-bit crypto.randomBytes value and account creation is rate-limited (DDPRateLimiter). Thanks to meifukun and xet7.
The OIDC login shared-serviceData race from the same report was already fixed earlier
(#4897 moved the per-login profile/serviceData/userinfo objects inside the OAuth
callback); the new source-guard test verifies it stays that way. Thanks to meifukun and xet7.
and adds the following new features:
- Added an automatic security/speed/tests remediation-logging subsystem (design docs docs/Security/Remediation/WeKan.md and FerretDB.md). WeKan’s runtime guards (SSRF, upload/avatar rejection and sanitization, forged X-Forwarded-For, export authz, invite rate-limit, …) now record each block/sanitize/remediate event into the existing WeKan database (a single
eventlogcollection, stream security/speed/tests) via normal Meteor JavaScript queries — no new files or databases under WRITABLE_PATH, so it works the same on FerretDB and MongoDB. FerretDB reports its problems to WeKan, which records them. Events use general category names plus the hall-of-fame *Bleed names. Thanks to xet7. - Added Admin Panel → Problems (a new button on the 2nd header bar to the right of the Info/version button, with a warning icon that turns red when there are new problems). The old Reports button is removed and its page becomes the Problems page: a left menu with Summary, Security, Speed and Tests above the moved Broken Cards/Files/Rules/Boards/Cards/Impersonation reports. Summary is a checkbox list of problem areas with one Acknowledge button (the only place to acknowledge, which resets the per-area new-problem count); Security/Speed/Tests are read-only, paginated, searchable event tables. Thanks to xet7.
and adds the following updates:
- Enabled FerretDB OpLog tailing by default so Meteor stops poll-and-diff, the main fix for FerretDB sitting at 100–390% CPU on busy boards. With FerretDB there is no MongoDB oplog, so Meteor re-ran every live query on a timer; FerretDB v1 now ships an OpLog (auto-created capped local.oplog.rs + replica-set hello handshake), so every FerretDB launch path starts ferretdb with —repl-set-name=rs0 and points WeKan at it via MONGO_OPLOG_URL, and Meteor TAILS the OpLog instead of polling. OpLog is used ONLY when tailing actually works: every platform that defaults to FerretDB — Snap, Sandstorm, the Docker image/compose and the prebuilt bundle — starts ferretdb with —repl-set-name and sets METEOR_REACTIVITY_ORDER=oplog,polling, so Meteor falls back to polling if the OpLog cannot be established (a broken/absent OpLog never blocks startup). Kill-switch WEKAN_FERRETDB_OPLOG=false reverts to polling only. Admin Panel → Version shows which driver is actually live (“Reactivity mode”) next to the configured METEOR_REACTIVITY_ORDER and DDP_TRANSPORT, so you can confirm OpLog came up rather than fell back. Also trimmed the client activity feed page from 500 to 50 rows (infinite scroll still loads more on demand). Thanks to uusijani, Nissulya and xet7.
- Updated SECURITY.md. Thanks to xet7.
and fixes the following bugs:
- Fix “Did not check() all arguments” server-log spam from the Admin Panel → Problems detail pages: the eventLogCount/eventLogPage methods awaited the admin check before check()ing their arguments, so a non-admin call (or one before admin status resolved) made Meteor’s audit-argument-checks mask the real error. check() now runs first in every argument-taking event-log method. Thanks to xet7.
- Fix MongoDB 3.x → FerretDB v1 migration failing on non-finite numbers: cards
whose
sortwas ±Infinity or NaN (written by old WeKan before #6472) were rejected by FerretDB/SQLite (“infinity values are not allowed”) and dropped, so boards stayed stuck on the loading spinner. The migration now clamps every non-finite double to a finite 0 before insert, and a newnonfinite-sort-repairstartup schema-upgrade step heals the same corruption in an already-running database (MongoDB or FerretDB). Thanks to Nissulya and xet7. - Fix LDAP
mail→emailfield mapping returning undefined (regression since the ldapjs → ldapts move): the sync read the mapped attribute case-sensitively while the rest of wekan-ldap reads it case-insensitively, so a directory that returns the attribute with a different case synced no email address. It is now read via getLDAPValue() like everywhere else. Thanks to Nissulya and xet7. - Fix a memory/listener leak in board and CSV export: each write paused on socket backpressure leaked one ‘error’ listener on the response (“MaxListenersExceededWarning: 11 error listeners added to [ServerResponse]”), which on a large export grew unbounded. Both listeners are now removed once the write settles. Thanks to uusijani and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.