From abffccabd273749f88a316ea4d20d3be100aecd5 Mon Sep 17 00:00:00 2001 From: Dusk1e Date: Fri, 29 May 2026 22:05:59 +0300 Subject: [PATCH] fix(gateway): respect own-policy group_policy when resolving unauthorized DM fallback --- gateway/run.py | 22 +++++++---- .../test_config_driven_access_policy.py | 37 ++++++++++++++++++- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/gateway/run.py b/gateway/run.py index 0549d7150a18..878d04540226 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -6802,10 +6802,11 @@ def _get_unauthorized_dm_behavior(self, platform: Optional[Platform]) -> str: 2. Explicit global ``unauthorized_dm_behavior`` in config — wins when no per-platform. 3. When an allowlist (``PLATFORM_ALLOWED_USERS``, ``PLATFORM_GROUP_ALLOWED_USERS`` / ``PLATFORM_GROUP_ALLOWED_CHATS``, - or ``GATEWAY_ALLOWED_USERS``) is configured, default to ``"ignore"`` — - the allowlist signals that the owner has deliberately restricted - access; spamming unknown contacts with pairing codes is both noisy - and a potential info-leak. (#9337) + or ``GATEWAY_ALLOWED_USERS``) or a config-driven own-policy + gateway restriction is configured, default to ``"ignore"`` — the + operator has deliberately restricted access, so spamming unknown + contacts with pairing codes is both noisy and a potential + info-leak. (#9337) 4. No allowlist and no explicit config → ``"pair"`` (open-gateway default). """ config = getattr(self, "config", None) @@ -6822,10 +6823,11 @@ def _get_unauthorized_dm_behavior(self, platform: Optional[Platform]) -> str: if config.unauthorized_dm_behavior != "pair": # non-default → explicit override return config.unauthorized_dm_behavior - # Config-driven dm_policy (WeCom / Weixin / Yuanbao / QQBot). An - # allowlist or disabled DM policy means the operator restricted access, - # so unauthorized DMs should be dropped silently rather than answered - # with a pairing code. An explicit pairing policy opts back into codes. + # Config-driven own-policy access control (WeCom / Weixin / Yuanbao / + # QQBot). Restrictive DM or group policies mean the operator narrowed + # access, so unauthorized DMs should be dropped silently rather than + # answered with a pairing code. An explicit pairing policy still opts + # back into codes. if platform and config and hasattr(config, "platforms"): platform_cfg = config.platforms.get(platform) extra = getattr(platform_cfg, "extra", None) if platform_cfg else None @@ -6835,6 +6837,10 @@ def _get_unauthorized_dm_behavior(self, platform: Optional[Platform]) -> str: return "pair" if dm_policy in {"allowlist", "disabled"}: return "ignore" + if self._adapter_enforces_own_access_policy(platform): + group_policy = str(extra.get("group_policy") or "").strip().lower() + if group_policy in {"allowlist", "disabled"}: + return "ignore" # No explicit override. Fall back to allowlist-aware default: # if any allowlist is configured for this platform, silently drop diff --git a/tests/gateway/test_config_driven_access_policy.py b/tests/gateway/test_config_driven_access_policy.py index 8659fb884e9f..2c2c417924f5 100644 --- a/tests/gateway/test_config_driven_access_policy.py +++ b/tests/gateway/test_config_driven_access_policy.py @@ -199,7 +199,7 @@ def test_unknown_adapter_does_not_crash_trust_check(monkeypatch): # --------------------------------------------------------------------------- -# Layer 3: unauthorized-DM behavior reads config dm_policy +# Layer 3: unauthorized-DM behavior reads config dm/group policy # --------------------------------------------------------------------------- @@ -222,6 +222,41 @@ def test_unauthorized_dm_behavior_follows_config_dm_policy(monkeypatch, dm_polic assert runner._get_unauthorized_dm_behavior(Platform.WECOM) == expected +@pytest.mark.parametrize("platform", _OWN_POLICY_PLATFORMS) +@pytest.mark.parametrize("group_policy", ["allowlist", "disabled"]) +def test_unauthorized_dm_behavior_follows_config_group_policy(monkeypatch, platform, group_policy): + """Restrictive own-policy group configs should silence stranger DMs too.""" + _clear_auth_env(monkeypatch) + config = GatewayConfig( + platforms={ + platform: PlatformConfig(enabled=True, extra={"group_policy": group_policy}) + } + ) + runner, _adapter = _make_runner(platform, config, enforces=True) + + assert runner._get_unauthorized_dm_behavior(platform) == "ignore" + + +@pytest.mark.parametrize("platform", _OWN_POLICY_PLATFORMS) +def test_explicit_pair_override_beats_group_policy_default(monkeypatch, platform): + """Per-platform pairing overrides the restrictive group-policy default.""" + _clear_auth_env(monkeypatch) + config = GatewayConfig( + platforms={ + platform: PlatformConfig( + enabled=True, + extra={ + "group_policy": "allowlist", + "unauthorized_dm_behavior": "pair", + }, + ) + } + ) + runner, _adapter = _make_runner(platform, config, enforces=True) + + assert runner._get_unauthorized_dm_behavior(platform) == "pair" + + def test_unauthorized_dm_behavior_open_policy_keeps_default(monkeypatch): """``dm_policy: open`` is not restrictive → falls through to the default.""" _clear_auth_env(monkeypatch)