fix(whatsapp): groupAllowFrom sender filter bypassed when groupPolicy is allowlist (#24670)

(cherry picked from commit af06ebd9a6)
This commit is contained in:
damaozi
2026-02-24 03:20:37 +08:00
committed by Peter Steinberger
parent 3f5e7f8156
commit c6bb7b0c04
4 changed files with 57 additions and 1 deletions

View File

@@ -81,6 +81,7 @@ Docs: https://docs.openclaw.ai
- Security/Config: redact sensitive-looking dynamic catchall keys in `config.get` snapshots (for example `env.*` and `skills.entries.*.env.*`) and preserve round-trip restore behavior for those redacted sentinels. Thanks @merc1305.
- Tests/Vitest: tier local parallel worker defaults by host memory, keep gateway serial by default on non-high-memory hosts, and document a low-profile fallback command for memory-constrained land/gate runs to prevent local OOMs. (#24719) Thanks @ngutman.
- WhatsApp/Group policy: fix `groupAllowFrom` sender filtering when `groupPolicy: "allowlist"` is set without explicit `groups` — previously all group messages were blocked even for allowlisted senders. (#24670)
- Agents/Context pruning: extend `cache-ttl` eligibility to Moonshot/Kimi and ZAI/GLM providers (including OpenRouter model refs), so `contextPruning.mode: "cache-ttl"` is no longer silently skipped for those sessions. (#24497) Thanks @lailoo.
- Tools/web_search: add `provider: "kimi"` (Moonshot) support with key/config schema wiring and a corrected two-step `$web_search` tool flow that echoes tool results before final synthesis, including citation extraction from search results. (#16616, #18822) Thanks @adshine.
- Media understanding/Video: add a native Moonshot video provider and include Moonshot in auto video key detection, plus refactor video execution to honor `entry/config/provider` baseUrl+header precedence (matching audio behavior). (#12063) Thanks @xiaoyaner0201.

View File

@@ -89,6 +89,46 @@ describe("resolveChannelGroupPolicy", () => {
expect(policy.allowlistEnabled).toBe(true);
expect(policy.allowed).toBe(false);
});
it("allows groups when groupPolicy=allowlist with hasGroupAllowFrom but no groups", () => {
const cfg = {
channels: {
whatsapp: {
groupPolicy: "allowlist",
},
},
} as OpenClawConfig;
const policy = resolveChannelGroupPolicy({
cfg,
channel: "whatsapp",
groupId: "123@g.us",
hasGroupAllowFrom: true,
});
expect(policy.allowlistEnabled).toBe(true);
expect(policy.allowed).toBe(true);
});
it("still fails closed when groupPolicy=allowlist without groups or groupAllowFrom", () => {
const cfg = {
channels: {
whatsapp: {
groupPolicy: "allowlist",
},
},
} as OpenClawConfig;
const policy = resolveChannelGroupPolicy({
cfg,
channel: "whatsapp",
groupId: "123@g.us",
hasGroupAllowFrom: false,
});
expect(policy.allowlistEnabled).toBe(true);
expect(policy.allowed).toBe(false);
});
});
describe("resolveToolsBySender", () => {

View File

@@ -328,6 +328,8 @@ export function resolveChannelGroupPolicy(params: {
groupId?: string | null;
accountId?: string | null;
groupIdCaseInsensitive?: boolean;
/** When true, sender-level filtering (groupAllowFrom) is configured upstream. */
hasGroupAllowFrom?: boolean;
}): ChannelGroupPolicy {
const { cfg, channel } = params;
const groups = resolveChannelGroups(cfg, channel, params.accountId);
@@ -340,8 +342,14 @@ export function resolveChannelGroupPolicy(params: {
: undefined;
const defaultConfig = groups?.["*"];
const allowAll = allowlistEnabled && Boolean(groups && Object.hasOwn(groups, "*"));
// When groupPolicy is "allowlist" with groupAllowFrom but no explicit groups,
// allow the group through — sender-level filtering handles access control.
const senderFilterBypass =
groupPolicy === "allowlist" && !hasGroups && Boolean(params.hasGroupAllowFrom);
const allowed =
groupPolicy === "disabled" ? false : !allowlistEnabled || allowAll || Boolean(groupConfig);
groupPolicy === "disabled"
? false
: !allowlistEnabled || allowAll || Boolean(groupConfig) || senderFilterBypass;
return {
allowlistEnabled,
allowed,

View File

@@ -16,10 +16,17 @@ export function resolveGroupPolicyFor(cfg: ReturnType<typeof loadConfig>, conver
ChatType: "group",
Provider: "whatsapp",
})?.id;
const whatsappCfg = cfg.channels?.whatsapp as
| { groupAllowFrom?: string[]; allowFrom?: string[] }
| undefined;
const hasGroupAllowFrom = Boolean(
whatsappCfg?.groupAllowFrom?.length || whatsappCfg?.allowFrom?.length,
);
return resolveChannelGroupPolicy({
cfg,
channel: "whatsapp",
groupId: groupId ?? conversationId,
hasGroupAllowFrom,
});
}