diff --git a/gateway/platforms/whatsapp_cloud.py b/gateway/platforms/whatsapp_cloud.py index 8b170db886..92c5f8767f 100644 --- a/gateway/platforms/whatsapp_cloud.py +++ b/gateway/platforms/whatsapp_cloud.py @@ -234,12 +234,12 @@ class WhatsAppCloudAdapter(WhatsAppBehaviorMixin, BasePlatformAdapter): """Normalize allowlist entries to bare wa_id (digits): strip ``@...`` JID suffixes and non-digits.""" return {re.sub(r"\D", "", entry.split("@", 1)[0]) or entry for entry in ids} - def _is_dm_allowed(self, sender_id: str) -> bool: - """Allowlist check against the normalized bare wa_id.""" - if self._dm_policy == "allowlist": - bare = re.sub(r"\D", "", str(sender_id).split("@", 1)[0]) - return (bare or sender_id) in self._normalize_allow_ids(self._live_dm_allow_from()) - return super()._is_dm_allowed(sender_id) + def _entry_matches(self, entries, target: str) -> bool: + """Bare-wa_id membership first (Cloud senders are digits), then the shared WhatsApp matcher + so ``*`` and phone/LID aliases keep working for DM intake and groups as they always did.""" + entries = set(entries or ()) + bare = re.sub(r"\D", "", str(target).split("@", 1)[0]) or target + return bare in self._normalize_allow_ids(entries) or super()._entry_matches(entries, target) def _allow_all_env_names(self) -> tuple[str, ...]: """Also honor the documented WHATSAPP_CLOUD_ALLOW_ALL_USERS opt-in.""" diff --git a/tests/gateway/test_access_policy_mixin.py b/tests/gateway/test_access_policy_mixin.py index 292daae97b..baf2406447 100644 --- a/tests/gateway/test_access_policy_mixin.py +++ b/tests/gateway/test_access_policy_mixin.py @@ -31,6 +31,7 @@ def _hosts(): """One bare instance per own-policy class, attributes set exactly as the adapters do.""" from gateway.platforms.qqbot.adapter import QQAdapter from gateway.platforms.weixin import WeixinAdapter + from gateway.platforms.whatsapp_cloud import WhatsAppCloudAdapter from gateway.platforms.whatsapp_common import WhatsAppBehaviorMixin from gateway.platforms.yuanbao import AccessPolicy from plugins.platforms.wecom.adapter import WeComAdapter @@ -40,9 +41,11 @@ def _hosts(): "wecom": object.__new__(WeComAdapter), "qqbot": object.__new__(QQAdapter), "whatsapp": WhatsAppBehaviorMixin(), + "whatsapp_cloud": object.__new__(WhatsAppCloudAdapter), "yuanbao": AccessPolicy("open", [], "open", []), } hosts["whatsapp"]._dm_allowlist_source = "config" + hosts["whatsapp_cloud"]._dm_allowlist_source = "config" hosts["wecom"]._groups = {} return hosts @@ -64,7 +67,9 @@ def _verdicts(host, name, dm_policy, group_policy): # would pass with a host whose prefix is missing — that is exactly how WeCom regressed once. PLATFORM_OPT_IN = {"weixin": "WEIXIN_ALLOW_ALL_USERS", "wecom": "WECOM_ALLOW_ALL_USERS", "qqbot": "QQ_ALLOW_ALL_USERS", "whatsapp": "WHATSAPP_ALLOW_ALL_USERS", - "yuanbao": "YUANBAO_ALLOW_ALL_USERS"} + "whatsapp_cloud": "WHATSAPP_CLOUD_ALLOW_ALL_USERS", "yuanbao": "YUANBAO_ALLOW_ALL_USERS"} +# Hosts whose allowlists document ``*`` (weixin/yuanbao match literally, as before). +WILDCARD_HOSTS = ("wecom", "qqbot", "whatsapp", "whatsapp_cloud") def _all_agree(hosts, opt_in_for, label): @@ -93,6 +98,20 @@ def test_all_own_policy_adapters_agree(monkeypatch, mode): _all_agree(_hosts(), opt_in_for, mode) +@pytest.mark.parametrize("name", WILDCARD_HOSTS) +def test_wildcard_allowlist_admits_strangers_on_every_path(name): + """``*`` must open strict DM auth, DM intake AND group intake alike — whatsapp_cloud once + honoured it on intake only, then on neither.""" + host = _hosts()[name] + host._dm_policy = host._group_policy = "allowlist" + host._allow_from, host._group_allow_from = ["*"], ["*"] + group_args = ("room-2", "stranger") if name in ("wecom", "qqbot") else ("room-2",) + assert host._is_dm_allowed("stranger") is True + assert host._is_dm_intake_allowed("stranger") is True + assert host._is_group_allowed(*group_args) is True + assert host._is_dm_intake_allowed(" ") is False + + def test_mixin_host_without_prefix_is_rejected_at_class_creation(): with pytest.raises(TypeError, match="ALLOW_ALL_ENV_PREFIX"): class Host(OwnAccessPolicyMixin): # noqa: F841