This release includes 4 security fixes for security teams reviewing exposed deployments.
Topics
+2 more
Affected surfaces
ReleasePort's take
Moderate signalThe v9.32 release adds input‑side validation for webhook URLs to prevent SSRF and enforces write‑level authorization for Custom Field mutations, among other security fixes.
Why it matters: Security fixes address CVEs with severity scores of 100 (GHSA-hc3x‑hq3m‑663q) and 90 (GHSA-6733‑4wgq‑8xvr), directly mitigating SSRF risk and privilege escalation for webhook integrations and custom field operations.
Summary
AI summaryBroad release touches https://github.com/wekan/wekan/commit/0e5fef6f31164fd3de4db353d04173ee0490cd65, SSRF, CWE-918, and https://github.com/wekan/wekan/commit/92beaa313706bc26fbea7e1cc8cfaf836609e038.
Changes in this release
| Type | Severity | Summary | CVE |
|---|---|---|---|
| Security | Critical |
Fixes GHSA-hc3x-hq3m-663q: adds input-side validation for webhook URLs, preventing SSRF. Fixes GHSA-hc3x-hq3m-663q: adds input-side validation for webhook URLs, preventing SSRF. Source: llm_adapter@2026-05-31 Confidence: high |
— |
| Security | Critical |
Fixes GHSA-7w2h-g83c-jqrp: requires board admin for copyBoard DDP method, preventing unauthorized board copying. Fixes GHSA-7w2h-g83c-jqrp: requires board admin for copyBoard DDP method, preventing unauthorized board copying. Source: llm_adapter@2026-05-31 Confidence: high |
— |
| Security | Critical |
Fixes GHSA-cv95-8h7c-2ffq: blocks direct client calls to six OIDC Meteor methods, preventing privilege escalation. Fixes GHSA-cv95-8h7c-2ffq: blocks direct client calls to six OIDC Meteor methods, preventing privilege escalation. Source: llm_adapter@2026-05-31 Confidence: high |
— |
| Security | Critical |
Fixes GHSA-mp7g-hj5q-gxhq: disables unconditional OIDC account merging, requiring verified email and opt‑in setting. Fixes GHSA-mp7g-hj5q-gxhq: disables unconditional OIDC account merging, requiring verified email and opt‑in setting. Source: llm_adapter@2026-05-31 Confidence: high |
— |
| Security | Critical |
Fixes GHSA-6733-4wgq-8xvr: enforces write‑level authz for Custom Field mutations, preventing read‑only privilege escalation. Fixes GHSA-6733-4wgq-8xvr: enforces write‑level authz for Custom Field mutations, preventing read‑only privilege escalation. Source: llm_adapter@2026-05-31 Confidence: high |
— |
| Feature | Low |
Adds test menu options for Playwright Chromium, Firefox, and WebKit in rebuild‑wekan.sh. Adds test menu options for Playwright Chromium, Firefox, and WebKit in rebuild‑wekan.sh. Source: llm_adapter@2026-05-31 Confidence: high |
— |
| Feature | Low |
Copies wepica style hide/show password UI to WeKan login and register pages. Copies wepica style hide/show password UI to WeKan login and register pages. Source: llm_adapter@2026-05-31 Confidence: high |
— |
| Feature | Low |
Adds global "Roles" tab in Admin Panel to control which board roles can invite users. Adds global "Roles" tab in Admin Panel to control which board roles can invite users. Source: llm_adapter@2026-05-31 Confidence: high |
— |
| Dependency | Low |
Updates multiple dependencies across eight commits, including meteor-node-stubs fork. Updates multiple dependencies across eight commits, including meteor-node-stubs fork. Source: llm_adapter@2026-05-31 Confidence: high |
— |
| Bugfix | Medium |
Restores external antivirus scanner functionality broken by avatar RCE fix. Restores external antivirus scanner functionality broken by avatar RCE fix. Source: llm_adapter@2026-05-31 Confidence: high |
— |
Full changelog
This release fixes the following CRITICAL SECURITY ISSUES:
- Fix GHSA-hc3x-hq3m-663q: Server-Side Request Forgery (SSRF) via webhook integration URLs (CWE-918).
Wekan's outgoing-webhook integrations let a board
admin store an arbitrary URL that is later fetched server-side, so a
caller-controlled URL pointing at an internal address (cloud metadata at
http://169.254.169.254/latest/meta-data/, loopback, RFC 1918 ranges, etc.)
could be used to reach internal services or exfiltrate data.- Originally fixed at the delivery layer in v8.35 and v8.36 (the
IntegrationBleed fixes).
Fix IntegrationBleed (v8.35)
routed webhook delivery inserver/notifications/outgoing.jsthrough
fetchSafe(server/lib/ssrfGuard.js), which validates the URL and blocks
private/loopback IPs, and
Fix RebindBleed of IntegrationBleed (v8.36)
hardened it further to resolve DNS once, pin the connection to the validated
IP, and block redirects — closing the actual request-time SSRF including DNS
rebinding. - New in this release: the missing input-side validation the advisory
requested, added at the REST write paths inserver/models/integrations.js.
Those endpoints accepted webhook URLs without a robust check:
POST /api/boards/:boardId/integrationsrelied only on the schema's regex
custom()validator (no DNS resolution, blind to a hostname that resolves to
a private IP and to decimal/octal/IPv4-mapped-IPv6 encodings), and
PUT /api/boards/:boardId/integrations/:intIdwrote the URL via
Integrations.direct.updateAsync, which bypasses schema validation entirely,
so updated URLs were never validated at the data layer. Both endpoints now run
the DNS-awarevalidateAttachmentUrl()
(models/lib/attachmentUrlValidation.js, the same validator already used for
attachment imports) before storing the URL, rejecting
private/loopback/link-local/reserved targets with HTTP 400. Together with the
v8.35/v8.36fetchSafedelivery guard and the schema validator on
client-side inserts/updates, webhook URLs are now validated on every write
path and again at delivery.
Thanks to Claude.
- Originally fixed at the delivery layer in v8.35 and v8.36 (the
- Fix GHSA-7w2h-g83c-jqrp: Authorization bypass in
copyBoardDDP method allows any user to copy private boards (CWE-862).
ThecopyBoardMeteor method inserver/publications/boards.js
had no authorization check: any logged-in user
could copy any board by ID — including private boards they are not a member of —
cloning all cards, checklists, custom fields, labels and rules, while the
equivalent REST endpointPOST /api/boards/:boardId/copycorrectly required
board admin. The method also looped over caller-suppliedproperties
(for (const key in properties) board[key] = properties[key]) and copied them
onto the new board, letting an attacker inject arbitrary fields such as
members(to add themselves as admin of the copy) orpermission: 'public'
(to expose the copy to everyone). The member-level fix shipped in v9.09 as part
of the AuthBleed fixes (caller must be authenticated, board must exist, caller
must be a board member, andmembers/permissionare stripped from
propertiesbefore the copy). This release tightens it further to full parity
with the REST endpoint:copyBoardnow requires the caller to be a board admin
(board.hasAdmin(this.userId), the same{isActive:true, isAdmin:true}
condition the REST API enforces viacheckAdminOrCondition) rather than merely
a member. - Fix GHSA-cv95-8h7c-2ffq: Missing authorization on OIDC Meteor methods allows privilege escalation to admin (CWE-269, CWE-862).
Six Meteor methods used internally by the OIDC login flow were registered as globally DDP-callable
with no authorization, while their non-OIDC counterparts require admin.setCreateOrgFromOidcand
setOrgAllFieldsFromOidc(server/models/org.js),setCreateTeamFromOidcand
setTeamAllFieldsFromOidc(server/models/team.js) let any authenticated user create/rename/
deactivate/modify arbitrary organizations and teams — includingorgAutoAddUsersWithDomainName—
bypassing the admin-only restriction. Most critically,groupRoutineOnLogin
(packages/wekan-oidc/oidc_server.js) setsisAdminfrom caller-supplied group data, so with
PROPAGATE_OIDC_DATAenabled any authenticated user could call it over DDP with
{groups:[{isAdmin:true,forceCreate:true}]}and promote themselves to global admin;
boardRoutineOnLogincould likewise add the caller to the default board. These six methods are only
ever invoked server-side (viaMeteor.callAsync) during the OIDC handshake, where a fix that checks
isAdmin/this.userIdwould break legitimate group/admin propagation (the user is not yet logged in
or admin at that point). Fixed by rejecting any direct client/DDP invocation: a server-to-server
Meteor.callAsyncruns withthis.connection === null, whereas a client call has a non-null
connection, so each of the six methods now throwsnot-authorizedwhenthis.connection !== null.
The legitimate OIDC login flow is unaffected.
Thanks to alexwaira for the coordinated disclosure, and Claude. - Fix GHSA-mp7g-hj5q-gxhq: OIDC Account Takeover via Unconditional Email-Based Account Merge in
Accounts.onCreateUserhook (CWE-287).
TheonCreateUserhook inserver/models/users.jsunconditionally merged an incoming OIDC login
into any existing Wekan account whose email or username matched the (attacker-controlled) OIDC
claims — no ownership check, no email-verification check, no notification. An attacker who could
present a matchingemail/usernameclaim (trivial on self-hosted Keycloak/Authentik, where the
email/email_verifiedclaims are attacker-settable) inherited the victim's_id, boards, cards,
attachments, API tokens and admin status, all during the attacker's own first OIDC login with no
victim interaction. Fixed to fail closed, mirroringLDAP_MERGE_EXISTING_USERS: matching is now by
email only (never username); auto-linking is opt-in via the newOAUTH2_MERGE_EXISTING_USERS
setting (OFF by default, so the default deployment never merges); even when enabled the OIDC
provider must assertemail_verified=true; otherwise the OIDC login is rejected with
oidc-email-already-in-useinstead of merging. The provider'semail_verifiedclaim is now
captured into the OIDC service data inpackages/wekan-oidc/oidc_server.js.
Thanks to alexwaira for the coordinated disclosure, and Claude. - Fix GHSA-6733-4wgq-8xvr: Read-only board members could create/modify/delete Custom Fields.
(privilege escalation via read-level authz on write operations, CWE-862).
All six mutating REST handlers inserver/models/customFields.js(POST/PUT custom-fields,
POST/PUT/DELETE dropdown-items, DELETE custom-fields) called the read-level
Authentication.checkBoardAccessinstead of the write-levelcheckBoardWriteAccess, letting a
board member with the read-only role (isReadOnly/isReadAssignedOnly) write Custom Field
data via the REST API whenWITH_API=true. Replaced the check withcheckBoardWriteAccessin
all six mutating handlers (the two GET handlers correctly stay oncheckBoardAccess), mirroring
lists.js/swimlanes.js/cards.js.
Thanks to Wernerina for the coordinated disclosure, and Claude. - Fix regression from the avatar RCE fix GHSA-35j7-h385-2q9g: external antivirus scanner broken (
asyncExecundefined).
The avatar RCE fix renamedasyncExectoasyncExecFileinmodels/fileValidation.js, but the
admin-configured external scanner command line still called the now-undefinedasyncExec, throwing
ReferenceError(swallowed by the catch) and making every upload silently fail validation whenever
an external scanner was configured. Restored a shell-basedasyncExecused only for that
admin-configured command line; MIME detection still uses the shell-freeasyncExecFile.
Thanks to Claude. - Fix CodeQL 68: Polynomial regex DoS in Jade parser.
Thanks to CodeQL and Claude. - Fix CodeQL 69: Polynomial regex DoS in Jade parser, part 2.
Remove the redundant$anchor from the interpolation regex incompiler.jsand bundledjade.js.
The greedy[\s\S]*already matches to end of string, so dropping$keeps the same match
while eliminating the backtracking that CodeQL alertjs/polynomial-redosflagged.
Thanks to CodeQL and Claude. - Fix CodeQL 63: Incomplete string escaping or encoding in bundled
jade.js.
In uglify-js'smake_string(bundled twice into jade's browser bundle), the chosen
quote was escaped in a trailingstr.replace(/'/g, "\\'")that CodeQL alert
js/incomplete-sanitizationflags for not escaping backslashes in that same call.
Backslashes were already escaped in the earlier single-passreplace, so this was a
local false positive, but the fix folds the quote escaping into that same pass
(escaping both quotes), making the escaping atomic and complete while keeping the
emitted string literals decode-equivalent.
Thanks to CodeQL and Claude. - Fix CodeQL 67: Incomplete multi-character sanitization.
Thanks to CodeQL and GitHub Copilot. - Fix CodeQL 60: Useless regular-expression character escape.
Thanks to CodeQL and GitHub Copilot. - Fix CodeQL 58: Incomplete multi-character sanitization.
Thanks to CodeQL and GitHub Copilot. - Fix CodeQL 57: Incomplete multi-character sanitization.
Thanks to CodeQL and GitHub Copilot. - Fix CodeQL 56: Incomplete string escaping or encoding](https://github.com/wekan/wekan/commit/c5e42607af5a0a396c0c85dba3652b8be28a2ff3).
Thanks to CodeQL and GitHub Copilot. - Fix CodeQL 55: Incomplete string escaping or encoding.
Thanks to CodeQL and GitHub Copilot. - Fix CodeQL 48: Clear-text logging of sensitive information.
Thanks to CodeQL and GitHub Copilot. - Fix CodeQL 418: Clear-text logging of sensitive information.
Thanks to CodeQL and GitHub Copilot. - Fix CodeQL 419: Clear-text logging of sensitive information.
Thanks to CodeQL and GitHub Copilot. - Delete calendar demos, so that CodeQL stops complaining.
Thanks to xet7. - Fix CodeQL 35: Insecure randomness.
Thanks to CodeQL and GitHub Copilot. - Fix CodeQL 417: Workflow does not contain permissions.
Thanks to CodeQL and GitHub Copilot.
and adds the following updates:
- Added test menu options.
"Test Playwright Chromium", "Test Playwright Firefox" and "Test
Playwright Webkit" torebuild-wekan.shfor running the
Playwright end-to-end test suite per browser.
Thanks to xet7. - Copy wepica style of hide/show password to WeKan login and register pages.
Thanks to Chostakovitch and xet7. - Updated dependencies.
Part 1,
Part 2,
Part 3,
Part 4,
Part 5,
Part 6: Fork and update meteor-node-stubs,
Part 7,
Part 8.
Thanks to developers of dependencies.
and adds the following new features:
- Add User board access roles to Admin Panel / People / Roles.
A new global "Roles" tab in Admin Panel / People lets a site admin choose which
board roles are allowed to invite users to a board ("Allow Invite to Board").
Each board role has its own toggle — Board Admin, Normal, Worker, Comment only,
No comments, Only Assigned Normal, Only Assigned Comment, Read Only and Only
Assigned Read — plus an "All Board Members" master toggle that selects them all.
The policy is enforced server-side in theinviteUserToBoardandsearchUsers
methods, and the add-member button in the board sidebar is shown only to roles
the policy allows. Global Admin Panel users (site admins) always have all rights
and cannot be restricted here; this is kept clearly distinct in code from the
per-board "Board Admin" role. Secure default: only Board Admin and Normal may invite.
Thanks to xet7.
and fixes the following bugs:
- Fix typos.
Thanks to xet7. - Fix iOS Chrome jQuery event handler runtime error.
Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
Security Fixes
- GHSA-hc3x-hq3m-663q – SSRF via webhook integration URLs (CWE‑918) fixed by DNS‑aware URL validation at write paths and fetchSafe guard
- GHSA-7w2h-g83c-jqrp – Authorization bypass in copyBoard DDP method now requires board admin
- GHSA-cv95-8h7c-2ffq – Missing authorization on OIDC Meteor methods rejected for client‑side calls
- GHSA-mp7g-hj5q-gxhq – Unconditional email/username merge during OIDC account creation prevented; now opt‑in, requires email_verified=true
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]