fix(whatsapp_cloud): allowlist "*" admits DM intake again
The mixin routes DM intake through _is_dm_allowed, and the Cloud override was a bare
wa_id set lookup with no "*" handling, so allow_from={"*"} (documented for
WHATSAPP_ALLOWED_USERS, inherited by Cloud) went from admitting intake on main to
denying it. Cloud now overrides _entry_matches instead: bare-wa_id membership, then
the shared WhatsApp matcher ("*" + aliases) — one predicate for strict DM auth, DM
intake and groups. whatsapp_cloud joins the parity matrix; a new wildcard row covers
every wildcard host on all three paths (sabotage: revert -> [whatsapp_cloud] fails).
This commit is contained in:
@@ -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."""
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user