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:
teknium1
2026-09-12 23:45:20 -07:00
committed by Teknium
parent 7370c82c2d
commit c0d7b05faa
2 changed files with 26 additions and 7 deletions

View File

@@ -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."""

View File

@@ -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