Skip to content

fix: reject dmPolicy="allowlist" with empty allowFrom across all channels#27936

Merged
steipete merged 3 commits intoopenclaw:mainfrom
widingmarcus-cyber:fix/allowlist-requires-allowfrom
Feb 26, 2026
Merged

fix: reject dmPolicy="allowlist" with empty allowFrom across all channels#27936
steipete merged 3 commits intoopenclaw:mainfrom
widingmarcus-cyber:fix/allowlist-requires-allowfrom

Conversation

@widingmarcus-cyber
Copy link
Copy Markdown
Contributor

Problem

When dmPolicy is set to "allowlist" but allowFrom is missing or empty, all DMs are silently dropped. No error appears in journalctl. The only indication is a validation warning buried in /tmp/openclaw/openclaw-*.log.

This is a common pitfall after upgrading from versions that supported external allowlist files (e.g., ~/.openclaw/telegram/allowlist-dm.json) to newer versions where allowFrom must be inline in openclaw.json.

Who is affected: Any user with dmPolicy: "allowlist" who upgrades and does not manually add allowFrom entries. Reported in #27892 as taking "several hours to debug" due to the silent failure.

Root Cause

The Zod schema validates dmPolicy="open" (requires allowFrom: ["*"]) but has no validation for dmPolicy="allowlist" with an empty/missing allowFrom. The config passes validation, the gateway starts, and messages are silently dropped at runtime in isSenderIdAllowed() because the allowlist is empty.

Fix

Schema validation (src/config/zod-schema.core.ts):

  • Added requireAllowlistAllowFrom() refinement helper (mirrors existing requireOpenAllowFrom)
  • When dmPolicy="allowlist" and allowFrom is empty/missing, config validation now fails with a clear error message

Applied to all channels (src/config/zod-schema.providers-core.ts, zod-schema.providers-whatsapp.ts):

  • Telegram (account + top-level)
  • Discord
  • Slack
  • Signal (account + top-level)
  • IRC
  • iMessage (account + top-level)
  • BlueBubbles (account + top-level)
  • MS Teams
  • Google Chat
  • WhatsApp (account + top-level)

Doctor detection (src/commands/doctor-config-flow.ts):

  • Added detectEmptyAllowlistPolicy() that scans all channel configs and warns:
    channels.telegram.dmPolicy is "allowlist" but allowFrom is empty - all DMs will be silently dropped
  • Surfaced in both openclaw doctor and openclaw doctor --fix flows

Testing

12 new test cases in config.allowlist-requires-allowfrom.test.ts:

  • Rejects dmPolicy="allowlist" without allowFrom (Telegram, Signal, Discord, WhatsApp)
  • Rejects dmPolicy="allowlist" with empty allowFrom: []
  • Accepts dmPolicy="allowlist" with valid allowFrom entries
  • Accepts dmPolicy="pairing" without allowFrom (no false positives)
  • Tests multi-account configs (Telegram accounts)

All 613 existing config tests + 117 doctor tests continue to pass.

@greptile-apps

Fixes #27892

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Feb 26, 2026

Greptile Summary

Prevents silent DM drops when dmPolicy="allowlist" is configured with empty allowFrom.

Key changes:

  • Added requireAllowlistAllowFrom() validation helper in src/config/zod-schema.core.ts:519-537 that rejects dmPolicy="allowlist" with empty/missing allowFrom
  • Applied validation across all core channels: Telegram, Discord, Slack, Signal, IRC, iMessage, BlueBubbles, MS Teams, Google Chat, and WhatsApp
  • Added detectEmptyAllowlistPolicy() to doctor flow (src/commands/doctor-config-flow.ts:1104-1162) to warn about this misconfiguration
  • Comprehensive test coverage in new test file with 12 test cases covering multiple channels and edge cases

How it works:
The schema validation now fails at config load time with a clear error message (e.g., 'channels.telegram.dmPolicy="allowlist" requires channels.telegram.allowFrom to contain at least one sender ID'), preventing users from accidentally running with a config that silently drops all DMs. The doctor command also detects existing configs with this issue and suggests fixes.

Confidence Score: 5/5

  • This PR is safe to merge with no identified risks
  • The implementation correctly addresses the silent failure issue with thorough validation across all channels, comprehensive test coverage (12 new tests plus 730 existing tests passing), and follows established project patterns. The fix is defensive (validation at config load time) and includes both schema validation and doctor detection. No logical errors, security issues, or breaking changes for valid configurations were found.
  • No files require special attention

Last reviewed commit: 8989baf

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8989baf3cc

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +231 to +234
requireAllowlistAllowFrom({
policy: value.dmPolicy,
allowFrom: value.allowFrom,
ctx,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Respect inherited allowFrom in account allowlist checks

Account configs inherit channel-level fields at runtime (for example, resolveTelegramAccount shallow-merges top-level and account values in src/telegram/accounts.ts), so an account can set dmPolicy: "allowlist" and rely on top-level allowFrom. This new refinement validates value.allowFrom on the account object alone, which now rejects previously working multi-account configs unless every account duplicates the allowlist; the same regression pattern is repeated for other account schemas in this change.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — removed account-level allowFrom validation from all 6 channel schemas (Telegram, Signal, IRC, iMessage, BlueBubbles, WhatsApp). Accounts now correctly inherit from parent. Top-level config schemas still enforce validation.

widingmarcus-cyber added a commit to widingmarcus-cyber/openclaw that referenced this pull request Feb 26, 2026
Account configs inherit channel-level fields at runtime (e.g.,
resolveTelegramAccount shallow-merges top-level and account values).
An account can set dmPolicy='allowlist' and rely on the parent's
allowFrom, so validating allowFrom on the account object alone
incorrectly rejects valid multi-account configs.

Removes requireAllowlistAllowFrom and requireOpenAllowFrom from all
account-level schemas (Telegram, Signal, IRC, iMessage, BlueBubbles).
Top-level config schemas still enforce the validation.

Addresses Codex review feedback on openclaw#27936.
@widingmarcus-cyber widingmarcus-cyber force-pushed the fix/allowlist-requires-allowfrom branch from 8989baf to d0cb8c0 Compare February 26, 2026 21:51
…nels

When dmPolicy is set to "allowlist" but allowFrom is missing or empty,
all DMs are silently dropped because no sender can match the empty
allowlist. This is a common pitfall after upgrades that change how
allowlist files are handled (e.g., external allowlist-dm.json files
being deprecated in favor of inline allowFrom arrays).

Changes:
- Add requireAllowlistAllowFrom schema refinement (zod-schema.core.ts)
- Apply validation to all channel schemas: Telegram, Discord, Slack,
  Signal, IRC, iMessage, BlueBubbles, MS Teams, Google Chat, WhatsApp
- Add detectEmptyAllowlistPolicy to doctor-config-flow.ts so
  "openclaw doctor" surfaces a clear warning with remediation steps
- Add 12 test cases covering reject/accept for multiple channels

Fixes openclaw#27892
Account configs inherit channel-level fields at runtime (e.g.,
resolveTelegramAccount shallow-merges top-level and account values).
An account can set dmPolicy='allowlist' and rely on the parent's
allowFrom, so validating allowFrom on the account object alone
incorrectly rejects valid multi-account configs.

Removes requireAllowlistAllowFrom and requireOpenAllowFrom from all
account-level schemas (Telegram, Signal, IRC, iMessage, BlueBubbles).
Top-level config schemas still enforce the validation.

Addresses Codex review feedback on openclaw#27936.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d0cb8c0657

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +120 to +123
enforceAllowlistDmPolicyAllowFrom({
dmPolicy: value.dmPolicy,
allowFrom: value.allowFrom,
ctx,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Allow account allowlist policy to inherit channel allowFrom

resolveWhatsAppAccount falls back to rootCfg.allowFrom when an account omits its own list (src/web/accounts.ts), so configs like channels.whatsapp.allowFrom=[...] with channels.whatsapp.accounts.work.dmPolicy="allowlist" are valid at runtime and worked before this commit. The new enforceAllowlistDmPolicyAllowFrom check validates the account object in isolation, so it now rejects those inherited multi-account setups unless every account duplicates allowFrom, causing unnecessary config-load failures.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in latest push — WhatsApp account schema no longer validates allowFrom independently. Parent channel allowFrom is inherited at runtime via resolveWhatsAppAccount.

Comment on lines +1153 to +1156
if (!account || typeof account !== "object") {
continue;
}
checkAccount(account, `channels.${channelName}.accounts.${accountId}`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass parent allowFrom context when scanning account warnings

detectEmptyAllowlistPolicy evaluates each account using only the account object’s allowFrom fields, so accounts that intentionally inherit channel-level allowlists are flagged as “all DMs will be silently dropped.” This is a false positive for merged-account channels (for example Telegram merges {...base, ...account} in src/telegram/accounts.ts), and it can mislead openclaw doctor users into changing already-correct configs.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — detectEmptyAllowlistPolicy now resolves parent-level allowFrom (both top-level and dm.allowFrom) before scanning accounts. No more false positives for inherited allowlists.

widingmarcus-cyber added a commit to widingmarcus-cyber/openclaw that referenced this pull request Feb 26, 2026
- Remove account-level allowFrom validation from WhatsAppAccountSchema
  (accounts inherit from parent via resolveWhatsAppAccount)
- Update detectEmptyAllowlistPolicy in doctor to pass parent allowFrom
  context when scanning accounts, preventing false positives

Addresses Codex P1+P2 review feedback on openclaw#27936.
@steipete steipete force-pushed the fix/allowlist-requires-allowfrom branch from a8fa463 to 1a46ce1 Compare February 26, 2026 22:04
@steipete steipete merged commit 45d8686 into openclaw:main Feb 26, 2026
5 checks passed
steipete pushed a commit that referenced this pull request Feb 26, 2026
Account configs inherit channel-level fields at runtime (e.g.,
resolveTelegramAccount shallow-merges top-level and account values).
An account can set dmPolicy='allowlist' and rely on the parent's
allowFrom, so validating allowFrom on the account object alone
incorrectly rejects valid multi-account configs.

Removes requireAllowlistAllowFrom and requireOpenAllowFrom from all
account-level schemas (Telegram, Signal, IRC, iMessage, BlueBubbles).
Top-level config schemas still enforce the validation.

Addresses Codex review feedback on #27936.
@steipete
Copy link
Copy Markdown
Contributor

Landed via temp rebase onto main.

  • Gate: pnpm check && pnpm build && pnpm test (local run hit existing unrelated failures in src/process/exec.test.ts and src/plugins/wired-hooks-compaction.test.ts)
  • Land commit: 1a46ce1
  • Merge commit: 45d8686

Thanks @widingmarcus-cyber!

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a46ce195d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +567 to +568
const effectiveAllowFrom =
account.allowFrom ?? account.dm?.allowFrom ?? value.allowFrom ?? value.dm?.allowFrom;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stop inheriting parent dm.allowFrom when account.dm is present

This fallback treats value.dm.allowFrom as inherited for account checks, but Discord/Slack account resolution is a shallow merge (mergeDiscordAccountConfig/mergeSlackAccountConfig), so an account-level dm object replaces the parent dm object entirely. In configs like parent dm.allowFrom + account dm.policy: "allowlist" (without account allowFrom), validation passes here, but runtime resolves an empty allowlist in the provider (discordCfg/slackCfg.allowFrom ?? dmConfig?.allowFrom) and silently blocks all DMs; this regression comes from the new effective-allowlist logic.

Useful? React with 👍 / 👎.

Comment on lines +1145 to +1146
const parentNestedAllowFrom = parentDm?.allowFrom as Array<string | number> | undefined;
const effectiveAllowFrom = topAllowFrom ?? nestedAllowFrom ?? parentNestedAllowFrom;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Respect account dm shadowing in doctor allowlist scan

The doctor scan always falls back to parentDm.allowFrom, but for Slack/Discord an account-level dm object shadows the parent dm object at runtime (shallow merge), so parent nested allowlists are not actually inherited in that case. This produces false negatives (no warning) for configs where account dm.policy is allowlist but account allowFrom is effectively empty, which leaves the same silent DM-drop behavior the doctor warning is supposed to catch.

Useful? React with 👍 / 👎.

mrosmarin added a commit to mrosmarin/openclaw that referenced this pull request Feb 26, 2026
* main:
  docs(cli): improve secrets command guide
  fix: repair Telegram allowlist DM migrations (openclaw#27936) (thanks @widingmarcus-cyber)
  Changelog: include Gemini OAuth PRs openclaw#16683 and openclaw#16684 (openclaw#27987)
  fix(ci): align sync boundary realpath canonicalization
  fix: unblock CI minimatch audit and host policy check
  fix(ios): eliminate Swift warnings and clean build logs
  chore(release): point appcast to beta tag
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 27, 2026
Account configs inherit channel-level fields at runtime (e.g.,
resolveTelegramAccount shallow-merges top-level and account values).
An account can set dmPolicy='allowlist' and rely on the parent's
allowFrom, so validating allowFrom on the account object alone
incorrectly rejects valid multi-account configs.

Removes requireAllowlistAllowFrom and requireOpenAllowFrom from all
account-level schemas (Telegram, Signal, IRC, iMessage, BlueBubbles).
Top-level config schemas still enforce the validation.

Addresses Codex review feedback on openclaw#27936.
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 27, 2026
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 27, 2026
Account configs inherit channel-level fields at runtime (e.g.,
resolveTelegramAccount shallow-merges top-level and account values).
An account can set dmPolicy='allowlist' and rely on the parent's
allowFrom, so validating allowFrom on the account object alone
incorrectly rejects valid multi-account configs.

Removes requireAllowlistAllowFrom and requireOpenAllowFrom from all
account-level schemas (Telegram, Signal, IRC, iMessage, BlueBubbles).
Top-level config schemas still enforce the validation.

Addresses Codex review feedback on openclaw#27936.

(cherry picked from commit 8df5999)
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 27, 2026
mylukin pushed a commit to mylukin/openclaw that referenced this pull request Feb 28, 2026
Account configs inherit channel-level fields at runtime (e.g.,
resolveTelegramAccount shallow-merges top-level and account values).
An account can set dmPolicy='allowlist' and rely on the parent's
allowFrom, so validating allowFrom on the account object alone
incorrectly rejects valid multi-account configs.

Removes requireAllowlistAllowFrom and requireOpenAllowFrom from all
account-level schemas (Telegram, Signal, IRC, iMessage, BlueBubbles).
Top-level config schemas still enforce the validation.

Addresses Codex review feedback on openclaw#27936.
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 28, 2026
Account configs inherit channel-level fields at runtime (e.g.,
resolveTelegramAccount shallow-merges top-level and account values).
An account can set dmPolicy='allowlist' and rely on the parent's
allowFrom, so validating allowFrom on the account object alone
incorrectly rejects valid multi-account configs.

Removes requireAllowlistAllowFrom and requireOpenAllowFrom from all
account-level schemas (Telegram, Signal, IRC, iMessage, BlueBubbles).
Top-level config schemas still enforce the validation.

Addresses Codex review feedback on openclaw#27936.

(cherry picked from commit 8df5999)
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 28, 2026
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 28, 2026
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 28, 2026
Account configs inherit channel-level fields at runtime (e.g.,
resolveTelegramAccount shallow-merges top-level and account values).
An account can set dmPolicy='allowlist' and rely on the parent's
allowFrom, so validating allowFrom on the account object alone
incorrectly rejects valid multi-account configs.

Removes requireAllowlistAllowFrom and requireOpenAllowFrom from all
account-level schemas (Telegram, Signal, IRC, iMessage, BlueBubbles).
Top-level config schemas still enforce the validation.

Addresses Codex review feedback on openclaw#27936.

(cherry picked from commit 8df5999)
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 28, 2026
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 28, 2026
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 28, 2026
Account configs inherit channel-level fields at runtime (e.g.,
resolveTelegramAccount shallow-merges top-level and account values).
An account can set dmPolicy='allowlist' and rely on the parent's
allowFrom, so validating allowFrom on the account object alone
incorrectly rejects valid multi-account configs.

Removes requireAllowlistAllowFrom and requireOpenAllowFrom from all
account-level schemas (Telegram, Signal, IRC, iMessage, BlueBubbles).
Top-level config schemas still enforce the validation.

Addresses Codex review feedback on openclaw#27936.

(cherry picked from commit 8df5999)
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 28, 2026
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 28, 2026
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 28, 2026
Account configs inherit channel-level fields at runtime (e.g.,
resolveTelegramAccount shallow-merges top-level and account values).
An account can set dmPolicy='allowlist' and rely on the parent's
allowFrom, so validating allowFrom on the account object alone
incorrectly rejects valid multi-account configs.

Removes requireAllowlistAllowFrom and requireOpenAllowFrom from all
account-level schemas (Telegram, Signal, IRC, iMessage, BlueBubbles).
Top-level config schemas still enforce the validation.

Addresses Codex review feedback on openclaw#27936.

(cherry picked from commit 8df5999)
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 28, 2026
wanjizheng pushed a commit to wanjizheng/openclaw that referenced this pull request Feb 28, 2026
vincentkoc pushed a commit to Sid-Qin/openclaw that referenced this pull request Feb 28, 2026
Account configs inherit channel-level fields at runtime (e.g.,
resolveTelegramAccount shallow-merges top-level and account values).
An account can set dmPolicy='allowlist' and rely on the parent's
allowFrom, so validating allowFrom on the account object alone
incorrectly rejects valid multi-account configs.

Removes requireAllowlistAllowFrom and requireOpenAllowFrom from all
account-level schemas (Telegram, Signal, IRC, iMessage, BlueBubbles).
Top-level config schemas still enforce the validation.

Addresses Codex review feedback on openclaw#27936.
vincentkoc pushed a commit to rylena/rylen-openclaw that referenced this pull request Feb 28, 2026
Account configs inherit channel-level fields at runtime (e.g.,
resolveTelegramAccount shallow-merges top-level and account values).
An account can set dmPolicy='allowlist' and rely on the parent's
allowFrom, so validating allowFrom on the account object alone
incorrectly rejects valid multi-account configs.

Removes requireAllowlistAllowFrom and requireOpenAllowFrom from all
account-level schemas (Telegram, Signal, IRC, iMessage, BlueBubbles).
Top-level config schemas still enforce the validation.

Addresses Codex review feedback on openclaw#27936.
vincentkoc pushed a commit to rylena/rylen-openclaw that referenced this pull request Feb 28, 2026
almoretti added a commit to almoretti/openclaw that referenced this pull request Feb 28, 2026
Groups configured with requireMention: 'monitor' are passive (hooks
fire, bot never responds), so sender-level filtering is unnecessary.

Uses a simple early return in WhatsApp access-control when a group is
in monitor mode — no changes to the shared dm-policy-shared layer.
This keeps the fix local and avoids adding parameters to the
centralized access control functions refactored in openclaw#24670 and openclaw#27936.

Each channel can add the same early return pattern to support monitor
groups, using the existing resolveChannelGroupRequireMention helper.
almoretti added a commit to almoretti/openclaw that referenced this pull request Feb 28, 2026
Groups configured with requireMention: 'monitor' are passive (hooks
fire, bot never responds), so sender-level filtering is unnecessary.

Uses a simple early return in WhatsApp access-control when a group is
in monitor mode — no changes to the shared dm-policy-shared layer.
This keeps the fix local and avoids adding parameters to the
centralized access control functions refactored in openclaw#24670 and openclaw#27936.

Each channel can add the same early return pattern to support monitor
groups, using the existing resolveChannelGroupRequireMention helper.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands Command implementations size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

upgrading from to 2026.2.25, Telegram DMs are silently dropped. The bot connects, polling works (offset file updates), but no messages are processed.

2 participants