Skip to content

wekan

v9.74 Security

This release includes 2 security fixes for security teams reviewing exposed deployments.

Published 21d Productivity & Wikis
✓ No known CVEs patched
Read the diff → Tool health → What is this tool? →
This release patches 2 known CVEs

Topics

docker javascript kanban meteor real-time sandstorm
+2 more
snapcraft wekan

Affected surfaces

rce_ssrf auth rbac

Summary

AI summary

Broad release touches user, https://github.com/wekan/wekan/commit/7d416cc7ccaf82876b052eeb621794f805e1e3d8, https://wekan.fi/hall-of-fame/excelbleed/, and https://wekan.fi/hall-of-fame/dnsbleed/.

Full changelog

v9.74 2026-07-05 WeKan ® release

This release fixes the following CRITICAL SECURITY ISSUES of
DnsBleed and
ExcelBleed:

  • DnsBleed — SSRF filter bypass via DNS-resolving hostname in outgoing webhooks
    (GHSA-66m2-4wfr-c45p, CWE-918).
    Incomplete-fix follow-up to WebhookBleed
    (GHSA-hc3x-hq3m-663q) and
    IntegrationBleed / RebindBleed.
    The synchronous URL validator on outgoing-webhook (board Integrations) URLs in
    models/integrations.js blocks private/loopback/link-local IPs by regex-matching the URL
    hostname string and never resolves DNS. A public hostname that resolves to a blocked address —
    e.g. 169-254-169-254.nip.io169.254.169.254 (cloud metadata), 127-0-0-1.nip.io127.0.0.1,
    or any attacker-controlled domain with an A/AAAA record pointing at an internal IP — passes that
    string blocklist. The report notes the underlying weakness: string matching is not an SSRF
    boundary
    because it can't see the resolved IP.

    • The reported PoC was already blocked at delivery by the earlier RebindBleed fix: outgoing
      webhooks are sent through fetchSafe (server/lib/ssrfGuard.js), which resolves the hostname,
      validates the resolved IP, pins the connection to it and blocks redirects — so
      169-254-169-254.nip.io is rejected before any request is made. The REST write paths
      (POST/PUT /api/boards/:boardId/integrations) were likewise already hardened in WebhookBleed to
      run the DNS-aware validateAttachmentUrl() at input time.
    • This release completes the fix and removes the drift risk the advisory points at. The
      delivery guard previously resolved only IPv4 A-records (dns.resolve4), leaving it blind to AAAA
      (an IPv6-only internal target was merely fail-closed, and legitimate IPv6 webhooks were
      unreachable) and keeping a second, less-complete private-range block-list that could drift out
      of sync with the input-time one. fetchSafe now resolves both address families via
      dns.lookup({ all: true }) — the same resolver call the input validator uses — and validates
      every resolved IP through the single shared isIpBlocked block-list in
      models/lib/attachmentUrlValidation.js. The three previously-separate block-lists (schema regex,
      delivery guard, input validator) are now one source of truth, and the schema-level url validator
      is documented in code as a non-authoritative first-line UI check only.
    • Affected Wekan v8.36 and later (input-side string validator); the delivery-time SSRF boundary has
      been in place since v8.35/v8.36 (IntegrationBleed) and v9.32 (WebhookBleed). Reported by
      4n207.
      Thanks to 4n207 and xet7!
  • ExcelBleed — broken access control in the Excel-export REST route lets any authenticated user export any private board
    (GHSA-mwq8-ccpm-r533,
    CWE-862 / CWE-639). Same un-awaited async-auth bug class as
    BFLABleed (48 REST endpoints, v9.22),
    CloneBleed (un-awaited allowIsBoardMemberByCard,
    v9.35) and TokenBleed.
    models/exportExcel.js called its access-control guard
    exporterExcel.canExport(user) without await. Because canExport is async, it returns a
    Promise (always truthy), so if (exporterExcel.canExport(user) || impersonateDone) was always true
    and exporterExcel.build(res) ran regardless of the guard's real result — any authenticated user
    could download the full contents of any board (card titles + descriptions, lists, swimlanes,
    members, metadata) via GET /api/boards/:boardId/exportExcel, including private boards they are not
    a member of. The JSON export route (/export) was correctly awaited and returned 403 for the same
    non-member.

    • Fixed by awaiting the guard —
      if ((await exporterExcel.canExport(user)) || impersonateDone) — matching every other export
      route (models/export.js, exportPDF.js, exportExcelCard.js, import.js). The Excel-export
      route was the lone remaining un-awaited canExport call site.
    • Affected Wekan v9.57.0 (latest) and earlier; present at HEAD until this release. Reported by
      sec-reex (defensive research, responsible disclosure, read-only PoC).
      Thanks to sec-reex and xet7!

and adds the following updates:

and fixed the following bugs:

  • Updated tests.
    Thanks to xet7.
  • Fix parallel tests.
    Thanks to xet7.
  • Fix tests.
    Thanks to xet7.
  • Board Settings / Card Settings / Checklist item count (0/0) on minicard. Default: Off.
    Thanks to carl-unique and xet7.
  • Card Settings popup options combined to same lines, and added more options.
    Thanks to xet7.
  • Fix tests.
    Thanks to xet7.
  • Playwright E2E: fixed three cross-process-contention flakes in the parallel run.
    The Chromium / Firefox / WebKit browser jobs run as separate processes against ONE shared server + DB,
    so specs that used fixed identifiers or global cleanups raced each other:
    • 26-shared-templates.e2e.js seeded a fixed email domain ([email protected]) in all three
      browsers, hitting E11000 duplicate key on the unique emails.address index. Now uses a
      unique-per-run token for the org / team / domain and template titles.
    • 38-impersonation.e2e.js cleaned up with a global
      deleteMany({ reason: 'clickedImpersonate' }) that deleted another browser's in-flight audit
      record mid-poll. Scoped the cleanup to the test's own adminId.
    • 32-org-team-feature-toggles.e2e.js exercises setAllOrgsFeature / setAllTeamsFeature, which
      do a global updateMany({}) across every org / team, so two browsers clobbered each other's
      rows. These browser-agnostic server-method tests now run in a single project (Chromium);
      Firefox / WebKit skip them.
      Thanks to xet7.
  • Server-side Mocha suite: fixed a startup crash and the 44 latent failures it had been hiding.
    • imports/i18n/i18n.test.js crashed the whole run at load: chai 6.x plugins (sinon-chai,
      chai-as-promised) are ESM-only, so use(require('sinon-chai')) handed chai.use() a module
      namespace instead of the plugin function ("fn is not a function"). Now imports the default export.
    • That unmasked a second load crash — PositionHistory.helpers is not a function: the
      meteor test entry (server/lib/tests/index.js) never ran the .helpers / .attachSchema shim
      that server/main.js bootstraps, so the first model to call Collection.helpers({...}) threw.
      The shim is now imported first in the test entry.
    • With the suite finally running, 44 server tests failed because meteor test only loads what the
      specs import (not the app's /server/imports). The specs now import the files that register the
      methods / globals under test (cards.vote / cards.pokerVote, api.attachment.*, cloneBoard,
      getBackgroundImageURL, applyListWidth, updateListSort, moveChecklist,
      userPositionHistory.*, archiveBoard, sendSMTPTestEmail, and the Attachments global).
      Also fixed genuine test bugs: the cards.vote / cards.pokerVote specs were written
      synchronously against async methods; the header-login trust specs restored env vars with
      process.env.X = undefined (which stores the string "undefined" and shadowed the trusted-IP
      allowlist, making every trusted source read as untrusted); the DnsBleed decimal-loopback matcher
      assumed the integer host survived URL normalisation; and the dependencies-OpenAPI spec could not
      locate its source file from the built bundle. The two cards.archive / cards.move specs tested
      Meteor methods that do not exist (archive / move are Minimongo document helpers secured by
      Cards.allow / Cards.deny, already covered by the cards security tests) and were removed.
      Server-side Mocha is now 409 passing, 0 failing.
      Thanks to xet7.
  • Fix translations at Login and Register pages.
    Thanks to xet7.

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

Security Fixes

  • GHSA-66m2-4wfr-c45p — DnsBleed: complete SSRF guard now resolves both IPv4 and IPv6 addresses via dns.lookup({all:true}) and uses a unified IP‑blocklist in attachmentUrlValidation.js
  • GHSA-mwq8-ccpm-r533 — ExcelBleed: fixed missing await on exporterExcel.canExport() to enforce access control for /api/boards/:boardId/exportExcel

Weekly OSS security release digest.

The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.

No spam, unsubscribe anytime.

Share this release

Track wekan

Get notified when new releases ship.

Sign up free

About wekan

The Open Source kanban, built with Meteor. GitHub issues/PRs are only for FLOSS Developers, not for support, support is at https://wekan.fi/commercial-support/ . New English strings for new features at imports/i18n/data/en.i18n.json . Non-English translations at https://app.transifex.com/wekan/wekan only.

All releases →

Related context

Beta — feedback welcome: [email protected]