Root cause
Authentication & access control
The new channel let each account inherit the channel-level webhook path, so named accounts that did not set their own path all resolved to one address and shared a single route under one account's policy.
How AI contributed: New attack surfacePublished Mar 26, 2026 in
openclaw/openclawAll 35 findings in this repository
Synology Chat supports several accounts under one channel, each with its own token, sender allowlist and direct-message policy. The account resolver merged each account's overrides over the channel configuration and let the webhook path fall back to the channel value, or to a fixed default when the channel set none, so a second account that did not name its own path came out with the same address as the first. The route registry keeps one handler per address, so the accounts shared a single route and the account that owned it answered for the others under its own rules. The channel first shipped in release 2026.2.22 and the behaviour is present through release 2026.3.13-1; the repair published in release 2026.3.22 refuses inherited and duplicate webhook paths at startup and stops replacing an existing route.
The channel resolved each account by merging its overrides over the channel settings, and the webhook path fell back to the channel value or to a fixed default. Every named account that did not override the path therefore came out with one address, and the route registry keeps a single handler per address, so the accounts collapsed onto one route: the account that owned that route applied its own sender allowlist and direct-message policy to traffic that belonged to the other accounts, which is the separation the advisory is about. The repair refuses to start an account that inherits a shared path in a multi-account setup or that resolves to a path another enabled account already uses, and it registers the route without replacing an existing one.
Root cause
The new channel let each account inherit the channel-level webhook path, so named accounts that did not set their own path all resolved to one address and shared a single route under one account's policy.
Fix
Startup now refuses an account that inherits a shared path in a multi-account setup or that resolves to a path another enabled account already uses, and the route is registered without replacing an existing one.
Fix by Peter Steinberger · no AI marker found
Case facts
@@ -0,0 +68,9 @@+ // Merge: account override > base channel config > env var+ return {+ accountId: id,+ enabled: accountOverride.enabled ?? channelCfg.enabled ?? true,+ token: accountOverride.token ?? channelCfg.token ?? envToken,+ incomingUrl: accountOverride.incomingUrl ?? channelCfg.incomingUrl ?? envIncomingUrl,+ nasHost: accountOverride.nasHost ?? channelCfg.nasHost ?? envNasHost,+ webhookPath: accountOverride.webhookPath ?? channelCfg.webhookPath ?? "/webhook/synology",+ dmPolicy: accountOverride.dmPolicy ?? channelCfg.dmPolicy ?? "allowlist",@@ -48,6 +55,38 @@ export function validateSynologyGatewayAccountStartup(params: { ); return { ok: false }; }+ const accountIds = listCombinedAccountIds({+ configuredAccountIds: Object.keys(cfg.channels?.["synology-chat"]?.accounts ?? {}),+ implicitAccountId:+ cfg.channels?.["synology-chat"]?.token || process.env.SYNOLOGY_CHAT_TOKEN+ ? DEFAULT_ACCOUNT_ID+ : undefined,+ });+ const isMultiAccount = accountIds.length > 1;+ if (+ isMultiAccount &&+ accountId !== DEFAULT_ACCOUNT_ID &&+ !account.hasExplicitWebhookPath &&+ !account.dangerouslyAllowInheritedWebhookPath+ ) {+ log?.warn?.(+ `Synology Chat account ${accountId} must set an explicit webhookPath in multi-account setups; refusing inherited shared path. Set channels.synology-chat.accounts.${accountId}.webhookPath or opt in with dangerouslyAllowInheritedWebhookPath=true.`,+ );+ return { ok: false };+ }+ const conflictingAccounts = accountIds.filter((candidateId) => {+ if (candidateId === accountId) {+ return false;+ }+ const candidate = resolveAccount(cfg, candidateId);+ return candidate.enabled && candidate.webhookPath === account.webhookPath;+ });+ if (conflictingAccounts.length > 0) {+ log?.warn?.(+ `Synology Chat account ${accountId} conflicts on webhookPath ${account.webhookPath} with ${conflictingAccounts.join(", ")}; refusing to start ambiguous shared route.`,+ );+ return { ok: false };+ } return { ok: true }; } @@ -73,7 +112,6 @@ export function registerSynologyWebhookRoute(params: { const unregister = registerPluginHttpRoute({ path: account.webhookPath, auth: "plugin",- replaceExisting: true, pluginId: CHANNEL_ID, accountId: account.accountId, log: (msg: string) => log?.info?.(msg),AI-assisted change ce3b44849652b429f774b654086bce283bf170e5aeb6ca04116fd8f9b42b382f · Fix adabb41c3afb785996b3e9ce46ee3808937ccbaa3d49e45affe7fb1133824fd9