Skip to content

Releases: wekan/wekan

v9.46

15 Jun 12:11

Choose a tag to compare

v9.46 2026-06-15 WeKan ® release

This release fixes the following CRITICAL SECURITY ISSUES of ProxyBleed:

  • Fixed ProxyBleed: Header-login IP allowlist bypass via X-Forwarded-For spoofing allows
    unauthenticated full account takeover (incl. admin)

    (GHSA-jggc-qvfc-jr6x, CWE-290 Authentication Bypass by Spoofing, CWE-348 Use of Less Trusted Source). WeKan's
    header-login (reverse-proxy SSO) feature gates passwordless login on a source-IP allowlist
    (HEADER_LOGIN_TRUSTED_IPS), but getRequestIp() in server/lib/headerLoginAuth.js derived the
    source IP from the client-supplied X-Forwarded-For request header (its left-most hop), falling
    back to the real TCP socket peer only when that header was absent. Because X-Forwarded-For is
    fully attacker-controlled, an unauthenticated attacker who can reach the app port directly (a
    second ingress, a published container port, a k8s NodePort, SSRF — i.e. any path that does not
    traverse the trusted reverse proxy) could send a single GET request with
    X-Forwarded-For: <an-allowlisted-ip> plus the username header (e.g. X-Auth-User: admin) and be
    minted a full passwordless login session (meteor_login_token) for any existing user, including
    admin — complete account takeover and admin impersonation, with no password, token or shared
    secret. An empty or unset allowlist additionally failed OPEN (it trusted every source). The minted
    session is a real, persisted resume token that also authenticates the REST API as the spoofed user.
    Fixed by deriving the source IP from the real TCP socket peer
    (req.socket.remoteAddress/req.connection.remoteAddress, normalizing IPv4-mapped IPv6) and never
    from X-Forwarded-For, and by making an empty/unset HEADER_LOGIN_TRUSTED_IPS fail CLOSED so
    header-login authenticates no one until the trusted reverse-proxy IP(s) are configured (a startup
    warning is logged in that case). The same findOrCreateHeaderLoginUser /
    isTrustedHeaderLoginSource helpers back the attachment API, so that path is fixed by the same
    change. OPERATOR ACTION REQUIRED: if you use header-login you must set HEADER_LOGIN_TRUSTED_IPS
    to your reverse proxy's IP address(es) — after this change an unset allowlist disables header-login
    instead of trusting everyone, and the source IP compared against the allowlist is now the real
    proxy connection, not a forgeable header. For multi-hop proxy deployments an OPT-IN
    HEADER_LOGIN_TRUSTED_PROXIES list was added: X-Forwarded-For is honored only when the immediate
    TCP peer is one of those explicitly trusted proxies, and then only the right-most hop that is not
    itself a trusted proxy (the real client) is matched against HEADER_LOGIN_TRUSTED_IPS — a direct
    attacker's forged header is still ignored. Covered by regression tests
    (server/lib/tests/proxybleed.security.tests.js and server/lib/tests/headerLoginAuth.tests.js),
    documented in Header-Login,
    and the new env vars were added to docker-compose.yml, start-wekan.sh, start-wekan.bat,
    Dockerfile, .devcontainer/Dockerfile, releases/virtualbox/start-wekan.sh and the Snap config.
    Affected Wekan v9.44 and earlier. Thanks to rz1027, xet7 and Claude.

and adds the following updates:

  • Improved accessibility across all pages following WCAG 2.1 AA guidelines:
    • Added a visible keyboard focus indicator (:focus-visible outline) for links,
      buttons and form controls. The global CSS reset previously stripped all focus
      outlines, leaving keyboard users with no indication of focus (WCAG 2.4.7).
    • Added a "Skip to main content" link that lets keyboard and screen-reader users
      bypass the header chrome (WCAG 2.4.1 Bypass Blocks).
    • Added landmark roles so assistive technology can navigate page regions:
      role="main" on the content area, role="navigation" on the header quick-access
      bar, and role="search" on the global search form (WCAG 1.3.1).
    • Marked modals and popups as role="dialog" with accessible names, set
      aria-modal="true" on modals, and moved keyboard focus into a modal when it
      opens and back to the triggering element when it closes (WCAG 2.4.3 Focus Order).
    • Added accessible names (aria-label) to icon-only controls that previously had
      none: modal/popup close and back buttons, the announcement close button, the
      sidebar close/back buttons and the global search input and clear button
      (WCAG 4.1.2 Name, Role, Value).
    • Marked decorative Font Awesome icons inside labelled controls as
      aria-hidden="true" so they are not announced redundantly.
    • Added a screen-reader-only .sr-only helper class.
  • Reorganized and updated the documentation:
    • Fixed 14 broken relative links in docs/README.md (verified with a link check).
    • Split the monolithic docs/Features/Features.md into focused topic pages under
      docs/Features/ subdirectories: Boards, Lists, Cards, Members, Keyboard
      Shortcuts, Admin Panel, WIP Limits, Cleanup and Stats; Features.md is now an
      index linking to them, and docs/README.md links the new pages.
    • Added documentation for previously-undocumented features: Right-to-Left (RTL)
      UI, Stickers, Card Locations, Board Background Images, Attachments and File
      Storage, the card "complete" checkbox, and a new Accessibility feature page
      (covering both the Admin Panel / Settings / Accessibility info page and the
      built-in accessibility features above).
    • Updated outdated information based on the CHANGELOG: the Meteor/Node versions
      (now Meteor 3.x / Node.js 24.x) and the obsolete feature wishlist (Custom
      Fields, Subtasks, Swimlanes, Gantt, WIP limits, voting and templates are now
      implemented).

and adds the following new features:

  • Added an accessibility end-to-end test suite
    that checks the page language, skip link, landmark roles, visible focus, dialog
    roles, accessible names on icon controls, and the absence of duplicate element ids.
  • Greatly expanded board automation Rules:
    • The Rules page is now a fullscreen page below the top bar (board sidebar →
      Rules), instead of a cramped popup. It has its own route /b/:id/:slug/rules.
    • Scheduled rules: run rules on a schedule (once on a date, or every day /
      weekday / week / month at a chosen time), on due dates (set / approaching /
      overdue), or by card aging ("a card has been in a list for N days"). A
      SyncedCron job evaluates these every minute. This makes processes such as
      "archive cards that have been in the Completed column for 90 days" work out of
      the box.
    • These long-standing automation feature requests are implemented by the above
      rules work and can be closed: Fixes #1160, Fixes #2476, Fixes #4372, Fixes #5775,
      Fixes #5825 — IFTTT-style rules including recurring cards (#1160), Trello-Butler-
      like Scheduled Rules (#2476), rules that set a due date 1 day/week/month ahead via
      the "set a date relative to now (+N days)" action (#4372), and automated recurring
      cards/tasks on a daily/weekday/weekly/monthly schedule from the GUI (#5775, #5825).
    • More Trello-Butler-style automations: card buttons (shown on the card)
      and board buttons (shown in the board header) that run an action on demand,
      and new actions — sort a list (by due/name/created/modified), move all cards in a
      list, mark card complete/incomplete, and set a date relative to now.
    • Manage rules: select all / unselect all, delete selected, edit (rename), and
      a drag-and-drop visual Workflow editor (Jira-like): drag a trigger and an
      action into a When … → Then … builder to create a rule, drag an action onto an
      existing rule to change it, and delete rules from the graph.
    • Import / Export rules to JSON (lossless) and CSV (round-trippable); export
      only the selected rules; a best-effort importer for Trello Butler commands
      (Trello board exports do not contain Butler rules, so they are pasted in and the
      supported subset is mapped, with unmapped lines reported).
    • Import visual workflows from n8n and Node-RED (best-effort: the workflow
      graph's trigger→action edges are mapped to WeKan rules, unmapped nodes reported),
      with an Import target selector to choose which personal workspace and
      board the imported rules go into (applies to all importers in the dialog).
  • Added a whole-board import REST API (POST /api/boards/import) that recreates a
    board — including its rules/triggers/actions (workflows) and other data — from a
    WeKan board export. With the existing export endpoint this enables migrating all
    boards + workflows + rules from another WeKan over the API
    ; api.py adds
    importboard and migratefromwekan REMOTE_URL REMOTE_USER REMOTE_PASS. The remote
    fetch is done client-side, so the server never fetches arbitrary URLs. Covered by
    an e2e test (export a board with a rule → import it → assert the rule, trigger and
    action are recreated on the new board) in tests/playwright/specs/23-rest-api-more.e2e.js.
    • Rules REST API to add/edit/remove/list rules
      (/api/boards/:boardId/rules), documented in the OpenAPI docs and api.py
      (addrule / editrule / removerule / listrules / getrule).
    • Added a Rul...
Read more

v9.45

14 Jun 06:53

Choose a tag to compare

v9.45 2026-06-14 WeKan ® release

This release adds the following updates:

  • [Made the "Release All Platforms" snap build resilient to transient Launchpad failures]{4a1eff2).
    snapcraft remote-build intermittently dies with a transient TLS
    drop (SSLEOFError: EOF occurred in violation of protocol, exit 70) while
    talking to Launchpad after pushing the repo — unrelated to the build itself —
    which failed the whole release. The step now retries the remote build up to
    three times before giving up.
    Thanks to xet7 and Claude.

Thanks to above GitHub users for their contributions and translators for their translations.

v9.44

14 Jun 06:11

Choose a tag to compare

v9.44 2026-06-14 WeKan ® release

This release adds the following new features:

  • Full right-to-left (RTL) UI for every page when an RTL language is selected
    (Arabic and its variants, Persian/Farsi, Hebrew, Uyghur, Uzbek-Arabic and
    Yiddish — the languages flagged rtl: true in imports/i18n/languages.js).
    Previously only some pages flipped. The root <html> element now gets a
    reactive dir="rtl"/dir="ltr" attribute that follows the chosen language, so
    the whole UI mirrors at once. To make this work on every page rather than a
    handful, all directional component CSS was converted to CSS logical properties
    (margin-leftmargin-inline-start, left:inset-inline-start:,
    text-align: lefttext-align: start, float: leftfloat: inline-start,
    and the mirror-image right/end variants) across 48 stylesheets, which flip
    automatically with dir. Horizontal-centering rules using
    left: 50% + transform: translate(-50%, …) were intentionally kept physical,
    since those already center correctly in both directions. The calendar view also
    renders RTL. Thanks to xet7 and Claude.
  • Tests for the RTL UI. A fast, server-less tests/rtl.test.js (run with
    node tests/rtl.test.js) checks that exactly the expected languages are
    flagged rtl: true, that the direction helper maps each language to the right
    dir value, that the root <html> and the client keep dir in sync, and —
    as a regression guard — that component CSS keeps using logical properties
    (no physical margin-left/float: left/bare left: offsets sneak back in).
    A Playwright browser spec tests/playwright/specs/18-rtl-layout.e2e.js drives
    the app in both English (LTR) and Arabic (RTL) and asserts, on the boards list,
    board view, card details, my-cards / due-cards / global-search, the admin
    settings page and the login page, that the direction is correct, the
    translated text is visible, and leading-edge content (the boards menu, the
    first board list) sits on the correct side. Thanks to xet7 and Claude.
  • Fixed the "Meteor unit tests" CI job hanging until its 45-minute timeout. The
    workflow set TEST_WATCH: '0', but meteortesting:mocha computes
    testWatch: TEST_WATCH || … and the string '0' is truthy in JavaScript, so
    it turned on watch mode and the process never exited. Removing the env var lets
    meteor test --once finish and exit with the correct status. Thanks to xet7
    and Claude.

Thanks to above GitHub users for their contributions and translators for their translations.

v9.43

13 Jun 17:31

Choose a tag to compare

v9.43 2026-06-13 WeKan ® release

This release adds the following new features:

  • Add/Edit location popup can also
    detect a location from a map link from many providers,
    grouped by region — USA: Google Maps, Bing Maps, Apple Maps, Waze;
    Europe: OpenStreetMap, HERE WeGo, Yandex Maps, Mapy.cz, 2GIS; Asia: Baidu Maps,
    Amap (Gaode) — plus generic ?q=/?ll= links: paste the link, press "Detect",
    and the latitude, longitude and (when present) the place name/address are
    filled in automatically. Detection handles each provider's real-world URL
    shapes, including percent-encoded commas (e.g. Waze/Yandex ll=...%2C...) and
    alternate forms (HERE share.here.com/l/, Yandex pt=, 2GIS m=), and the
    coordinate order (several non-US providers use lon,lat). For the Chinese
    providers a datum conversion is applied — Baidu uses BD-09 and Amap uses
    GCJ-02, both offset from WGS-84 — so pins land in the right place on the way in
    and out. The popup also has an "Open map links at" setting (OpenStreetMap by
    default) saved to the user profile, with the same region-grouped provider list,
    controlling which map service the location "Open in map" links use.
    Thanks to xet7 and Claude.

and fixes the following bugs:

  • Fix incomplete URL substring sanitization.

Thanks to above GitHub users for their contributions and translators for their translations.

v9.42

13 Jun 14:45

Choose a tag to compare

v9.42 2026-06-13 WeKan ® release

This release adds the following new features:

  • Greatly improved import from Trello to WeKan:
    • Attachments now import. Trello attachment URLs require OAuth to download, so
      WeKan could not fetch them. The Trello import page now has two separate file
      fields: a .json field for a single Trello board, and a
      .zip field for a package produced by the
      Trello Card Attachments Downloader.
      The .zip may contain one or more board .json files together with each
      board's attachments in a board-name subfolder. The .zip is uploaded over HTTP
      and processed entirely on the server (the attachment bytes never travel over
      the realtime WebSocket, which previously overflowed the connection and made
      the page flicker): WeKan detects every board .json, matches each board to its
      attachment subfolder by name, streams each file straight into the Default
      file storage, imports all boards, then opens All Boards. The upload is guarded
      against abuse — it rejects oversized uploads, zip bombs (too many files or too
      much uncompressed data, per-file size caps) and unsafe entry paths (absolute
      paths or .. traversal), and never overwrites existing files (stored under
      generated ids). The importer also reads attachments from card.attachments
      (newer Trello exports), not only from the action log, and uses the correct
      Meteor-Files insert API (the previous calls were no-ops on Meteor 3). Single
      board imports (the .json file or the pasted JSON, after the map-members step)
      now run through the same server-side HTTP path rather than the realtime
      importBoard method, so a heavy import can no longer drop the WebSocket and
      get retried in an endless loop (the page flickering with "Invalid frame
      header" / repeated "logged out" errors). When importing only a single board
      .json and you have saved Trello API key and token, WeKan uses them on the
      server to download that board's attachments, background image, member
      avatars and card stickers from Trello (a .json export often omits stickers;
      avatars only fill in mapped users who have none — an existing avatar is never
      overwritten); each download is best-effort and a failure never aborts the
      import. After a single-board import the new board's data is subscribed and
      loaded before opening it, so it shows its swimlanes, lists and cards
      immediately instead of needing a browser page reload (the HTTP import, unlike
      the old realtime method, does not push the written documents to the client by
      itself).
    • Import Trello-only card features: stickers (shown as Font Awesome icons on
      minicards and the card detail), card cover (color or attachment), card
      location (name, address, coordinates with an OpenStreetMap link), and the
      due-date "complete" checkbox shown on the minicard. Trello's two named
      sticker packs are renamed and highlighted (the icon keeps its normal colour)
      so they stay distinct: the "taco" pack becomes "mascot" with an underlined
      icon, and the "pete" pack becomes "computer" with a ring around the icon,
      e.g. "pete-ghost" imports as "computer ghost" with a Snapchat-ghost icon.
      Several named stickers that used to share an icon now get their own Font
      Awesome 4.7 icon (mascot active => heartbeat, pixel => qrcode, proto =>
      flask, embarrassed => meh-o, clean => bath, computer shipped => truck).
    • Live Trello API import: paste your Trello API key and token on the Trello
      import page to list all your Trello workspaces and boards, then import all
      or selected boards together with their attachments (downloaded
      server-side). Imported boards are placed under a personal workspace named
      after their Trello workspace, created by name if it does not exist, under an
      optional parent workspace you choose. The API client respects Trello's rate
      limits: it spaces out requests, honours Retry-After on HTTP 429, retries
      transient 5xx and network errors with capped exponential backoff, and
      reports a clear error on an invalid key/token (401). After a successful API
      import you are taken to the All Boards page where the new boards appear.
    • The live API import runs as a background job on the server: you can navigate
      away and come back to the Trello import page to watch progress, see
      per-board results, and read the full error log. Errors are shown in a
      selectable text box with a one-click "Copy to clipboard" button (and you can
      still select and copy just part of the text) so they are easy to share when
      fixing issues. If the import stops on a fatal API error (invalid token, or
      rate limit still failing after retries) you can fix the cause and Resume
      from where it left off, and a server restart leaves the job paused for
      resuming rather than lost. You can also Cancel the import, or Cancel and
      delete the boards already imported, to start the whole process over cleanly.
    • You can optionally save your Trello API key and token with the Save and
      Delete buttons on the import page. When saved, they are stored only on the
      server, never sent back to the browser, and are reused automatically when
      you list workspaces or import (so you do not have to paste them again); a
      "saved" indicator is shown, Save can overwrite them with new values, and
      Delete clears them from the database. If you do not save them, they are kept
      only in server memory for the running import and re-supplied when you resume.
    • Larger Trello JSON exports can be selected as a file instead of pasted.
    • Keep imported usernames for later mapping. Imported members that match an
      existing WeKan user (by username, or by a previously recorded imported
      username) are mapped automatically, and the imported username is remembered
      on that user so future imports map too. Members with no matching WeKan user
      are no longer lost: their usernames are kept on the board, so an admin can
      map them to real users later in Admin Panel / People (Imported Usernames),
      without having to map everyone up front at import time.
      Thanks to xet7 and Claude.
  • Board background images can now be stored in WeKan. A new backgrounds file
    storage directory is created alongside attachments and avatars (using the
    current default storage backend), and Board menu → Board backgrounds lets a
    board admin upload background images, set one as the active board background,
    download, and delete them. Board export now includes the board's background
    images and re-imports them, and a Trello board's background image is
    downloaded and stored on import (so it keeps working even if the original
    Trello URL later changes). Thanks to xet7 and Claude.
  • Stickers can be added to and removed from cards directly in WeKan, chosen from
    a set of Font Awesome icons similar to Trello's stickers (previously stickers
    only arrived via Trello import). The picker also includes every "mascot"
    (underlined) and "computer" (ringed) highlighted sticker that a Trello import
    can produce — generated from the same icon mapping the importer uses — so any
    imported sticker can also be added by hand. Stickers show on the minicard and
    in the card detail. Thanks to xet7 and Claude.
  • The Trello-style "complete" checkbox (mark a card complete/incomplete,
    independent of the due date) is shown as an animated green checkbox to the
    left of the card title, both on the minicard and in the opened card, with
    "Mark as complete" / "Mark as incomplete" tooltips. It uses the same animated
    checkbox style as Admin Panel / Settings / Announcements and is vertically
    centered with the title text, and the two stay in sync. The minicard and the
    opened-card checkboxes use the same checked style (the board theme's colour, or
    green when no theme is set), so they always look identical. Subtask checkboxes
    and the card-detail custom-field checkbox now use the same animated checkbox as
    checklist items (they previously used static square icons), so every checkbox
    on a card animates consistently. Thanks to xet7 and Claude.
  • Cards can now have multiple locations, similar to multiple members. The card
    detail shows a Location section (after Labels and Stickers) listing each
    location with its name, address and an OpenStreetMap link, with an "Add
    location" button to add more and a button to edit or remove each one. A single
    location imported from Trello keeps working and is shown in the same list. The
    Add/Edit location popup can also detect a location from any map link (Google
    Maps, OpenStreetMap, Bing Maps, Apple Maps, or generic ?q=/?ll= links):
    paste the link, press "Detect", and the latitude, longitude and (when present)
    the place name/address are filled in automatically. The popup also has an
    "Open map links at" setting (OpenStreetMap by default, or Google/Bing/Apple
    Maps) saved to the user profile, controlling which map service the location
    "Open in map" links use. Thanks to xet7 and Claude.
  • The opened card now docks to the top of the window, overlaying the global and
    board header bars, instead of opening downward from the clicked minicard, and
    it can be dragged all the way to the top without its top hiding behind those
    bars. (At 100% zoom the board wrapper no longer sets a transform: scale(1),
    which had made it the containing block for the fixed card and trapped it below
    the headers.) Thanks to xet7 and Claude.

and fixes the following bugs:

Read more

v9.41

11 Jun 22:24

Choose a tag to compare

v9.41 2026-06-12 WeKan ® release

This release adds the following new features:

Thanks to above GitHub users for their contributions and translators for their translations.

v9.40

11 Jun 18:42

Choose a tag to compare

v9.40 2026-06-11 WeKan ® release

This release fixes the following bugs:

Thanks to above GitHub users for their contributions and translators for their translations.

v9.39

11 Jun 18:26

Choose a tag to compare

v9.39 2026-06-11 WeKan ® release

This release fixes the following bugs:

Thanks to above GitHub users for their contributions and translators for their translations.

v9.38

11 Jun 18:00

Choose a tag to compare

v9.38 2026-06-11 WeKan ® release

This release fixes the following bugs:

Thanks to above GitHub users for their contributions and translators for their translations.

v9.37

11 Jun 14:41

Choose a tag to compare

v9.37 2026-06-11 WeKan ® release

This release fixes the following CRITICAL SECURITY ISSUES of BoardBleed:

  • Fixed BoardBleed:
    Broken access control lets any authenticated user move their Cards/Lists/Swimlanes into
    a private board they are not a member of (cross-board write via collection
    allow rule)](GHSA-gm7v-pc38-53jr)
    (CWE-284, CWE-639). WeKan boards are membership-scoped, but the DDP collection write
    policies for Cards, Lists and Swimlanes (server/permissions/cards.js,
    server/permissions/lists.js, server/permissions/swimlanes.js) authorized an update by
    checking only the CURRENT (pre-update) boardId of the document — i.e. the attacker's own
    source board — and never validated the NEW boardId supplied in the update modifier.
    Because every logged-in user can create a board where they are admin, an attacker could take
    a document they own and, in a single /cards/update, /lists/update or /swimlanes/update
    DDP call, $set its boardId (plus swimlaneId/listId) to a victim's private board: the
    allow rule saw the attacker's own source board, approved the write, and the document was
    relocated into a board the attacker is not a member of and cannot even read. This let an
    unprivileged user inject arbitrary cards/lists/swimlanes (attacker-controlled titles,
    descriptions, assignees, etc.) into any private board by id, defeating board-level access
    control. The REST API for the same operation
    (PUT /api/boards/:boardId/lists/:listId/cards/:cardId with newBoardId) was not affected
    because it correctly calls Authentication.checkBoardWriteAccess(req.userId, newBoardId) on
    the destination board; only the DDP allow/deny layer was vulnerable. Fixed by adding a
    denyCrossBoardMove helper in server/lib/utils.js and a Cards.deny/Lists.deny/
    Swimlanes.deny update rule on each collection that rejects any update whose modifier
    $sets a boardId on which the caller does not have write access, so a cross-board move is
    only allowed into a destination board where the user is an active write-capable member.
    Affected Wekan v9.35 and earlier. Thanks to 0xzap, xet7 and Claude.

and adds the following updates:

Thanks to above GitHub users for their contributions and translators for their translations.