Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion plugins/platforms/whatsapp/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,12 @@ def __init__(self, config: PlatformConfig):
self._dm_policy = str(config.extra.get("dm_policy") or os.getenv("WHATSAPP_DM_POLICY", "pairing")).strip().lower()
self._allow_from = self._coerce_allow_list(config.extra.get("allow_from") or config.extra.get("allowFrom"))
self._group_policy = str(config.extra.get("group_policy") or os.getenv("WHATSAPP_GROUP_POLICY", "pairing")).strip().lower()
self._group_allow_from = self._coerce_allow_list(config.extra.get("group_allow_from") or config.extra.get("groupAllowFrom"))
self._group_allow_from = self._coerce_allow_list(
config.extra.get("group_allow_from")
or config.extra.get("groupAllowFrom")
or os.getenv("WHATSAPP_GROUP_ALLOW_FROM")
or os.getenv("WHATSAPP_GROUP_ALLOWED_USERS")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This truthiness chain makes an explicit group_allow_from: [] or "" fall through to the environment and can widen an allowlist from a stale env value. Select an env fallback only when neither config key is present, and add regressions for explicit empty snake_case and camelCase config values.

)
self._mention_patterns = self._compile_mention_patterns()
self._message_queue: asyncio.Queue = asyncio.Queue()
self._bridge_log_fh = None
Expand Down
87 changes: 87 additions & 0 deletions tests/gateway/test_whatsapp_group_env_fallback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""Verify that the WhatsApp group allowlist reads env vars as fallback.

Regression test for #56767: after the built-in β†’ plugin migration,
``WHATSAPP_GROUP_ALLOW_FROM`` / ``WHATSAPP_GROUP_ALLOWED_USERS`` were no
longer read, causing group messages to be silently dropped when the user
configured the allowlist via ``.env`` instead of ``config.yaml``.
"""

from gateway.config import Platform, PlatformConfig

from plugins.platforms.whatsapp.adapter import WhatsAppAdapter


# --- Env-only fallback (the core regression) ---


def test_group_allowlist_reads_whatsapp_group_allow_from_env(monkeypatch):
"""Env-only setup: ``WHATSAPP_GROUP_ALLOW_FROM`` must populate the
group allowlist when ``config.yaml`` has no ``group_allow_from``."""
monkeypatch.setenv("WHATSAPP_GROUP_ALLOW_FROM", "12036300111@g.us, 12036300222@g.us")
adapter = WhatsAppAdapter(PlatformConfig(enabled=True, extra={}))
assert adapter._group_allow_from == {"12036300111@g.us", "12036300222@g.us"}


def test_group_allowlist_reads_whatsapp_group_allowed_users_env(monkeypatch):
"""Env-only setup: ``WHATSAPP_GROUP_ALLOWED_USERS`` (setup-wizard name)
must also populate the group allowlist."""
monkeypatch.setenv("WHATSAPP_GROUP_ALLOWED_USERS", "12036300333@g.us")
adapter = WhatsAppAdapter(PlatformConfig(enabled=True, extra={}))
assert adapter._group_allow_from == {"12036300333@g.us"}


# --- Config takes precedence over env ---


def test_group_allowlist_config_extra_wins_over_env(monkeypatch):
"""Explicit ``group_allow_from`` in config must not be widened by a
stale env var."""
monkeypatch.setenv("WHATSAPP_GROUP_ALLOW_FROM", "12036300999@g.us")
adapter = WhatsAppAdapter(PlatformConfig(
enabled=True, extra={"group_allow_from": ["12036300444@g.us"]},
))
assert adapter._group_allow_from == {"12036300444@g.us"}


def test_group_allowlist_camelcase_config_wins_over_env(monkeypatch):
"""CamelCase ``groupAllowFrom`` in config extra also takes precedence."""
monkeypatch.setenv("WHATSAPP_GROUP_ALLOWED_USERS", "12036300999@g.us")
adapter = WhatsAppAdapter(PlatformConfig(
enabled=True, extra={"groupAllowFrom": ["12036300555@g.us"]},
))
assert adapter._group_allow_from == {"12036300555@g.us"}


# --- Env var priority: GROUP_ALLOW_FROM checked first ---


def test_group_allow_from_checked_before_group_allowed_users(monkeypatch):
"""When both env vars are set, ``WHATSAPP_GROUP_ALLOW_FROM`` wins
(checked first in the ``or`` chain)."""
monkeypatch.setenv("WHATSAPP_GROUP_ALLOW_FROM", "12036300666@g.us")
monkeypatch.setenv("WHATSAPP_GROUP_ALLOWED_USERS", "12036300777@g.us")
adapter = WhatsAppAdapter(PlatformConfig(enabled=True, extra={}))
assert adapter._group_allow_from == {"12036300666@g.us"}


# --- Empty / missing env var ---


def test_group_allowlist_empty_when_no_config_no_env(monkeypatch):
"""No config and no env β†’ empty allowlist (default)."""
adapter = WhatsAppAdapter(PlatformConfig(enabled=True, extra={}))
assert adapter._group_allow_from == set()


# --- End-to-end: env-only allowlist gates group messages ---


def test_env_only_allowlist_accepts_listed_group(monkeypatch):
"""Env-only ``group_policy=allowlist`` + ``group_allow_from`` lets a
listed group through via ``_is_group_allowed``."""
monkeypatch.setenv("WHATSAPP_GROUP_POLICY", "allowlist")
monkeypatch.setenv("WHATSAPP_GROUP_ALLOW_FROM", "120363001234567890@g.us")
adapter = WhatsAppAdapter(PlatformConfig(enabled=True, extra={}))
assert adapter._group_allow_from == {"120363001234567890@g.us"}
assert adapter._is_group_allowed("120363001234567890@g.us") is True
assert adapter._is_group_allowed("999999999999@g.us") is False
Loading