This release includes 2 security fixes for security teams reviewing exposed deployments.
Topics
+2 more
Affected surfaces
Summary
AI summaryBroad 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.jsblocks 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.io→169.254.169.254(cloud metadata),127-0-0-1.nip.io→127.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 throughfetchSafe(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.iois 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-awarevalidateAttachmentUrl()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.fetchSafenow 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 sharedisIpBlockedblock-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-levelurlvalidator
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!
- The reported PoC was already blocked at delivery by the earlier RebindBleed fix: outgoing
-
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-awaitedallowIsBoardMemberByCard,
v9.35) and TokenBleed.
models/exportExcel.jscalled its access-control guard
exporterExcel.canExport(user)withoutawait. BecausecanExportisasync, it returns a
Promise (always truthy), soif (exporterExcel.canExport(user) || impersonateDone)was always true
andexporterExcel.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) viaGET /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-awaitedcanExportcall 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!
- Fixed by awaiting the guard —
and adds the following updates:
- Update to Meteor 3.5.
Thanks to Meteor developers. - Run all tests at parallel or sequential.
Thanks to xet7. - Run all tests at parallel or sequential.
Thanks to xet7. - Script to be executeable.
Thanks to xet7. - Update vscode sandbox.
Thanks to xet7. - Each run all tests now has logs at ../log/YYYY-MM-DD_HH-MM-SS/.
Thanks to xet7.
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.jsseeded a fixed email domain ([email protected]) in all three
browsers, hittingE11000 duplicate keyon the uniqueemails.addressindex. Now uses a
unique-per-run token for the org / team / domain and template titles.38-impersonation.e2e.jscleaned 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 ownadminId.32-org-team-feature-toggles.e2e.jsexercisessetAllOrgsFeature/setAllTeamsFeature, which
do a globalupdateMany({})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.jscrashed the whole run at load:chai6.x plugins (sinon-chai,
chai-as-promised) are ESM-only, souse(require('sinon-chai'))handedchai.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 testentry (server/lib/tests/index.js) never ran the.helpers/.attachSchemashim
thatserver/main.jsbootstraps, so the first model to callCollection.helpers({...})threw.
The shim is now imported first in the test entry. - With the suite finally running, 44 server tests failed because
meteor testonly 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 theAttachmentsglobal).
Also fixed genuine test bugs: thecards.vote/cards.pokerVotespecs 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 twocards.archive/cards.movespecs tested
Meteor methods that do not exist (archive / move are Minimongo document helpers secured by
Cards.allow/Cards.deny, already covered by thecards securitytests) 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
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.
Beta — feedback welcome: [email protected]