Skip to content

fix(whatsapp): read WHATSAPP_ALLOWED_USERS and WHATSAPP_GROUP_ALLOWED_USERS from env - #37452

Closed
AhmetArif0 wants to merge 1 commit into
NousResearch:mainfrom
AhmetArif0:fix/whatsapp-env-allowlist
Closed

AhmetArif0 wants to merge 1 commit into
NousResearch:mainfrom
AhmetArif0:fix/whatsapp-env-allowlist

Conversation

@AhmetArif0

Copy link
Copy Markdown
Contributor

Summary

dm_policy honors WHATSAPP_DM_POLICY from the environment and group_policy honors WHATSAPP_GROUP_POLICY, but _allow_from and _group_allow_from were only populated from config.extra — env-only setups silently got empty allowlists.

Concrete failure path:

  1. User runs the CLI setup wizard → wizard calls save_env_value("WHATSAPP_ALLOWED_USERS", phone) and writes it to .env
  2. User sets WHATSAPP_DM_POLICY=allowlist (or the wizard does so)
  3. WhatsAppAdapter.__init__ reads dm_policy from env correctly but _allow_from stays set() because config.extra.get("allow_from") returns None and there was no env fallback
  4. Every authorized DM is dropped at intake

Same issue existed for WHATSAPP_GROUP_ALLOWED_USERS / group_allow_from.

Fix

Add env fallbacks mirroring dm_policy / group_policy:

self._allow_from = self._coerce_allow_list(
    config.extra.get("allow_from")
    or config.extra.get("allowFrom")
    or os.getenv("WHATSAPP_ALLOWED_USERS", "")
)
self._group_allow_from = self._coerce_allow_list(
    config.extra.get("group_allow_from")
    or config.extra.get("groupAllowFrom")
    or os.getenv("WHATSAPP_GROUP_ALLOWED_USERS", "")
)

Config extra still takes precedence over env so an explicit allowlist cannot be silently widened.

This is the exact same fix applied to WeCom in f7a3509 (fix(gateway): honor WECOM_ALLOWED_USERS in env-only WeCom DM allowlist), merged today.

Evidence that env vars are expected to work

  • .env.example:352: # WHATSAPP_ALLOWED_USERS=15551234567
  • hermes_cli/main.py: setup wizard writes WHATSAPP_ALLOWED_USERS to .env
  • gateway/config.py:1091-1098: bridges YAML allow_from / group_allow_from into WHATSAPP_ALLOWED_USERS / WHATSAPP_GROUP_ALLOWED_USERS env vars
  • _coerce_allow_list docstring: "Parse allow_from / group_allow_from from config or env var" — the "or env var" was unimplemented

Test plan

  • test_dm_allowlist_honors_env_only_whatsapp_allowed_users — env-only DM allowlist works
  • test_dm_allowlist_extra_takes_precedence_over_env — config.extra wins for DMs
  • test_group_allowlist_honors_env_only_whatsapp_group_allowed_users — env-only group allowlist works
  • test_group_allowlist_extra_takes_precedence_over_env — config.extra wins for groups
  • All 30 existing WhatsApp gating tests still pass

…_USERS from env

dm_policy honors WHATSAPP_DM_POLICY and group_policy honors
WHATSAPP_GROUP_POLICY from the environment, but _allow_from and
_group_allow_from were only populated from config.extra — env-only
setups were silently left with empty allowlists.

The CLI setup wizard (hermes_cli/main.py) saves WHATSAPP_ALLOWED_USERS
to .env, .env.example documents it, and gateway/config.py bridges the
YAML allow_from field into WHATSAPP_ALLOWED_USERS. Any of these paths
followed by dm_policy=allowlist resulted in every authorized DM being
dropped at intake.

Mirrors the WeCom fix in f7a3509 which resolved the identical gap
for WECOM_ALLOWED_USERS. Config extra still takes precedence over env
so an explicit allowlist cannot be widened by a stale env var.

Adds four regression tests: env-only populates allowlist (DM + group),
extra takes precedence over env (DM + group).
@rodriguez46p-ui

Copy link
Copy Markdown

Hermes Agent Review

Found one precedence edge case in the allowlist fallback logic:

  • config.extra.get("allow_from") or ... or os.getenv("WHATSAPP_ALLOWED_USERS") treats an explicit empty config allowlist (allow_from: [] or allow_from: "") as falsy, so it falls through to the env var and can widen access from a stale WHATSAPP_ALLOWED_USERS value.
  • Same concern applies to group_allow_from.

I verified on this PR branch that PlatformConfig(extra={"dm_policy":"allowlist", "allow_from": []}) plus WHATSAPP_ALLOWED_USERS=env-user@s.whatsapp.net produces _allow_from == {"env-user@s.whatsapp.net"} and allows that env user.

Suggested fix: distinguish missing config keys from explicit empty values, e.g. choose env only when neither snake_case nor camelCase key is present in config.extra.

Local targeted test suite otherwise passes: python -m pytest tests/gateway/test_whatsapp_group_gating.py -q -o 'addopts='30 passed.


Reviewed by Hermes Agent hourly commander.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery platform/whatsapp WhatsApp Business adapter area/config Config system, migrations, profiles labels Jun 2, 2026

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review Summary

Verdict: Approved

Clean fix: makes WhatsApp adapter read WHATSAPP_ALLOWED_USERS and WHATSAPP_GROUP_ALLOWED_USERS from environment variables as fallback when config extra doesn't specify them. Config extra properly takes precedence over env vars to prevent accidental widening.

✅ Looks Good

  • +4 tests covering DM allowlist env, group allowlist env, and precedence behavior
  • Well-documented inline comments
  • Fixes silent failure mode where dm_policy: allowlist without extra.allow_from would drop all DMs
  • No security concerns — env-only setup was the missing piece

Reviewed by Hermes Agent

@teknium1 teknium1 left a comment

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.

Thanks for identifying a real env-only allowlist gap. The issue remains on current main, but this branch predates the WhatsApp adapter migration.

Problems

  • The diff changes gateway/platforms/whatsapp.py, while current main constructs the live adapter in plugins/platforms/whatsapp/adapter.py:392-412 (migrated by 560010547). The current defect is still at plugins/platforms/whatsapp/adapter.py:410 and :412, so the patch needs relocation.
  • The proposed config.extra.get(...) or os.getenv(...) chain makes explicit empty config values fall through to the environment. That can widen an intentionally empty DM or group allowlist; distinguish an absent key from an empty configured value.
  • The proposed tests import the removed gateway.platforms.whatsapp module; current tests import plugins.platforms.whatsapp.adapter at tests/gateway/test_whatsapp_group_gating.py:9.

Suggested changes

  • Port the fix and tests to the plugin adapter, preserving explicit empty config values, then add regression coverage for empty DM and group lists with populated environment values.

Automated hermes-sweeper review.

@@ -261,9 +261,17 @@ def __init__(self, config: PlatformConfig):
))
self._reply_prefix: Optional[str] = config.extra.get("reply_prefix")
self._dm_policy = str(config.extra.get("dm_policy") or os.getenv("WHATSAPP_DM_POLICY", "open")).strip().lower()

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 lets an explicit empty allow_from ([] or "") fall through to WHATSAPP_ALLOWED_USERS, widening access from a stale environment value. Resolve by key presence so an explicitly configured empty list remains authoritative; apply the same rule to the group allowlist.

@kshitijk4poor

Copy link
Copy Markdown
Contributor

The env read this PR asked for landed on main via #115360 (merge 8925c70a1c): the adapter now reads WHATSAPP_GROUP_ALLOWED_USERS (after WHATSAPP_GROUP_ALLOW_FROM) through the shared _select_allowlist reader and exports the resolved list to the bridge. You were the first to submit this fix (June 2); the stack was built from the later #111067 before I found this PR, so the git author on that commit is @sal — I'm sorry the credit didn't land on your name in history. Recording it here and in the #80660 close. Closing as fixed on main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/whatsapp WhatsApp Business adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants