ForcePilot/backend/test/unit/channels/test_channels_imessage_security.py

346 lines
13 KiB
Python
Raw Normal View History

from __future__ import annotations
import pytest
from yuxi.channels.adapters.imessage.security import (
DmPolicy,
GroupPolicy,
IMessageSecurityPolicy,
SecurityCheckResult,
resolve_dm_policy,
resolve_group_policy,
_match_entry,
_normalize_handle,
_strip_prefix,
)
class TestResolveDmPolicy:
def test_explicit_pairing(self):
assert resolve_dm_policy({"dmPolicy": "pairing"}) == DmPolicy.PAIRING
def test_explicit_open(self):
assert resolve_dm_policy({"dm_policy": "open"}) == DmPolicy.OPEN
def test_explicit_disabled(self):
assert resolve_dm_policy({"dmPolicy": "disabled"}) == DmPolicy.DISABLED
def test_explicit_allowlist(self):
assert resolve_dm_policy({"dm_policy": "allowlist"}) == DmPolicy.ALLOWLIST
def test_invalid_explicit_falls_back(self):
assert resolve_dm_policy({"dm_policy": "invalid"}) == DmPolicy.OPEN
def test_allow_from_with_wildcard(self):
assert resolve_dm_policy({"allowFrom": ["*"]}) == DmPolicy.OPEN
def test_allow_from_with_entries(self):
assert resolve_dm_policy({"allowFrom": ["+8613800138000"]}) == DmPolicy.ALLOWLIST
def test_empty_allow_from(self):
assert resolve_dm_policy({"allowFrom": []}) == DmPolicy.OPEN
def test_no_policy_specified(self):
assert resolve_dm_policy({}) == DmPolicy.OPEN
class TestResolveGroupPolicy:
def test_explicit_open(self):
assert resolve_group_policy({"groupPolicy": "open"}) == GroupPolicy.OPEN
def test_explicit_disabled(self):
assert resolve_group_policy({"group_policy": "disabled"}) == GroupPolicy.DISABLED
def test_explicit_allowlist(self):
assert resolve_group_policy({"groupPolicy": "allowlist"}) == GroupPolicy.ALLOWLIST
def test_invalid_explicit_falls_back(self):
assert resolve_group_policy({"groupPolicy": "invalid"}) == GroupPolicy.ALLOWLIST
def test_no_policy_specified(self):
assert resolve_group_policy({}) == GroupPolicy.ALLOWLIST
class TestNormalizeHandle:
def test_removes_plus(self):
assert _normalize_handle("+8613800138000") == "8613800138000"
def test_removes_spaces(self):
assert _normalize_handle(" +86 138 0013 8000 ") == "8613800138000"
def test_email_preserved(self):
assert _normalize_handle("test@example.com") == "test@example.com"
class TestStripPrefix:
def test_no_prefix(self):
assert _strip_prefix("+8613800138000") == ("+8613800138000", None)
def test_imessage_prefix(self):
assert _strip_prefix("imessage:+8613800138000") == ("+8613800138000", "imessage")
def test_sms_prefix(self):
assert _strip_prefix("sms:+8613800138000") == ("+8613800138000", "sms")
def test_chat_id_prefix(self):
assert _strip_prefix("chat_id:test123") == ("test123", "chat_id")
def test_chat_guid_prefix(self):
assert _strip_prefix("chat_guid:test456") == ("test456", "chat_guid")
def test_prefix_case_insensitive(self):
assert _strip_prefix("IMESSAGE:+8613800138000") == ("+8613800138000", "imessage")
class TestMatchEntry:
def test_wildcard(self):
assert _match_entry("anyone", ["*"]) is True
def test_exact_simple_match(self):
assert _match_entry("+8613800138000", ["+8613800138000"]) is True
def test_exact_match_with_imessage_prefix(self):
assert _match_entry("+8613800138000", ["imessage:+8613800138000"]) is True
def test_exact_match_with_sms_prefix(self):
assert _match_entry("+8613800138000", ["sms:+8613800138000"]) is True
def test_no_match(self):
assert _match_entry("+8613800138000", ["+8613900139000"]) is False
def test_email_match(self):
assert _match_entry("test@example.com", ["test@example.com"]) is True
def test_chat_guid_match(self):
assert _match_entry("iMessage;-;group001", ["chat_guid:iMessage;-;group001"]) is True
def test_empty_allow_list(self):
assert _match_entry("+8613800138000", []) is False
class TestSecurityCheckResult:
def test_allowed(self):
r = SecurityCheckResult(allowed=True)
assert r.allowed is True
assert r.reject_reason is None
assert r.reply is None
def test_blocked_with_reason(self):
r = SecurityCheckResult(allowed=False, reject_reason="not_in_allowlist", reply="Unauthorized")
assert r.allowed is False
assert r.reject_reason == "not_in_allowlist"
assert r.reply == "Unauthorized"
class TestIMessageSecurityPolicy:
def make_policy(self, **overrides):
config = {
"dmPolicy": "pairing",
"groupPolicy": "allowlist",
"allowFrom": ["+8613800138000"],
"groupAllowFrom": ["iMessage;-;group001"],
**overrides,
}
return IMessageSecurityPolicy(config)
def test_initialization(self):
policy = self.make_policy()
assert policy.dm_policy == DmPolicy.PAIRING
assert policy.group_policy == GroupPolicy.ALLOWLIST
assert len(policy.allow_list) > 0
assert len(policy.group_allow_list) > 0
def test_check_dm_open(self):
policy = self.make_policy(dmPolicy="open")
result = policy.check_dm_access("+8613900139000")
assert result.allowed is True
def test_check_dm_disabled(self):
policy = self.make_policy(dmPolicy="disabled")
result = policy.check_dm_access("+8613800138000")
assert result.allowed is False
assert "disabled" in str(result.reject_reason)
def test_check_dm_allowlist_allowed(self):
policy = self.make_policy(dmPolicy="allowlist", allowFrom=["+8613800138000"])
result = policy.check_dm_access("+8613800138000")
assert result.allowed is True
def test_check_dm_allowlist_blocked(self):
policy = self.make_policy(dmPolicy="allowlist", allowFrom=["+8613800138000"])
result = policy.check_dm_access("+8613900139000")
assert result.allowed is False
assert "not_in_dm_allowlist" in str(result.reject_reason)
def test_check_dm_pairing(self):
policy = self.make_policy(dmPolicy="pairing")
result = policy.check_dm_access("+8613900139000")
assert result.allowed is True
def test_check_group_open(self):
policy = self.make_policy(groupPolicy="open")
result = policy.check_group_access("iMessage;-;group002")
assert result.allowed is True
def test_check_group_disabled(self):
policy = self.make_policy(groupPolicy="disabled")
result = policy.check_group_access("iMessage;-;group001")
assert result.allowed is False
def test_check_group_allowlist_allowed(self):
policy = self.make_policy(groupPolicy="allowlist", groupAllowFrom=["iMessage;-;group001"])
result = policy.check_group_access("iMessage;-;group001")
assert result.allowed is True
def test_check_group_allowlist_blocked(self):
policy = self.make_policy(groupPolicy="allowlist", groupAllowFrom=["iMessage;-;group001"])
result = policy.check_group_access("iMessage;-;group002")
assert result.allowed is False
def test_check_group_override_disabled(self):
policy = self.make_policy(
groupPolicy="open",
groups={"iMessage;-;group001": {"enabled": False}},
)
result = policy.check_group_access("iMessage;-;group001")
assert result.allowed is False
def test_check_mention_required_not_required(self):
policy = self.make_policy(requireMention=False)
result = policy.check_mention_required("iMessage;-;group001", None, "")
assert result.allowed is True
def test_check_mention_required_bot_mentioned(self):
policy = self.make_policy(requireMention=True)
from yuxi.channels.models import MentionsInfo
mentions = MentionsInfo(mentioned_user_ids=["+8613800138000"])
result = policy.check_mention_required(
"iMessage;-;group001", mentions, "+8613800138000"
)
assert result.allowed is True
def test_check_mention_required_bot_not_mentioned(self):
policy = self.make_policy(requireMention=True)
from yuxi.channels.models import MentionsInfo
mentions = MentionsInfo(mentioned_user_ids=["+8613900139000"])
result = policy.check_mention_required(
"iMessage;-;group001", mentions, "+8613800138000"
)
assert result.allowed is False
def test_add_to_allow_list(self):
policy = self.make_policy(allowFrom=["+8613800138000"])
policy.add_to_allow_list("+8613900139000")
assert "8613900139000" in str(policy.allow_list)
def test_add_duplicate_noop(self):
policy = self.make_policy(allowFrom=["+8613800138000"])
length_before = len(policy.allow_list)
policy.add_to_allow_list("+8613800138000")
assert len(policy.allow_list) == length_before
def test_remove_from_allow_list(self):
policy = self.make_policy(allowFrom=["+8613800138000", "+8613900139000"])
assert policy.remove_from_allow_list("+8613800138000") is True
assert policy.remove_from_allow_list("nonexistent") is False
def test_add_to_group_allow_list(self):
policy = self.make_policy(groupAllowFrom=[])
policy.add_to_group_allow_list("iMessage;-;group_new")
assert "iMessage;-;group_new" in policy.group_allow_list
def test_remove_from_group_allow_list(self):
policy = self.make_policy(
groupAllowFrom=["iMessage;-;group001", "iMessage;-;group002"]
)
assert policy.remove_from_group_allow_list("iMessage;-;group001") is True
assert policy.remove_from_group_allow_list("nonexistent") is False
def test_collect_warnings_dm_open(self):
policy = self.make_policy(dmPolicy="open")
warnings = policy.collect_warnings()
assert any("open" in w.lower() for w in warnings)
def test_collect_warnings_allowlist_empty(self):
policy = self.make_policy(dmPolicy="allowlist", allowFrom=[])
warnings = policy.collect_warnings()
assert any("empty" in w.lower() for w in warnings)
def test_collect_warnings_group_allowlist_empty(self):
policy = self.make_policy(groupPolicy="allowlist", groupAllowFrom=[])
warnings = policy.collect_warnings()
assert any("empty" in w.lower() for w in warnings)
def test_collect_warnings_group_open(self):
policy = self.make_policy(groupPolicy="open", groupAllowFrom=[])
warnings = policy.collect_warnings()
assert len(warnings) >= 1
def test_collect_warnings_fallback_enabled(self):
policy = self.make_policy(groupAllowFromFallback=True)
warnings = policy.collect_warnings()
assert any("fallback" in w.lower() for w in warnings)
def test_resolve_runtime_group_policy_explicit(self):
policy = self.make_policy(
groups={"iMessage;-;group001": {"group_policy": "open"}}
)
result = policy.resolve_runtime_group_policy("iMessage;-;group001")
assert result == GroupPolicy.OPEN
def test_resolve_runtime_group_policy_default(self):
policy = self.make_policy(groupPolicy="allowlist")
result = policy.resolve_runtime_group_policy("iMessage;-;nonexistent")
assert result == GroupPolicy.ALLOWLIST
def test_resolve_pinned_dm_owner(self):
policy = self.make_policy(allowFrom=["+8613800138000"])
owner = policy.resolve_pinned_dm_owner()
assert owner == "8613800138000"
def test_resolve_pinned_dm_owner_with_prefix(self):
policy = self.make_policy(allowFrom=["imsg:+8613800138000"])
owner = policy.resolve_pinned_dm_owner()
assert owner is not None
def test_context_visibility_open(self):
policy = self.make_policy(contextVisibilityMode="open")
assert policy.evaluate_context_visibility(
"iMessage;+;chat001", "+8613900139000"
) is True
def test_context_visibility_disabled(self):
policy = self.make_policy(contextVisibilityMode="disabled")
assert policy.evaluate_context_visibility(
"iMessage;+;chat001", "+8613800138000"
) is False
def test_context_visibility_allowlist_matched(self):
policy = self.make_policy(
contextVisibilityMode="allowlist", allowFrom=["+8613800138000"]
)
assert policy.evaluate_context_visibility(
"iMessage;+;chat001", "+8613800138000"
) is True
def test_context_visibility_allowlist_not_matched(self):
policy = self.make_policy(
contextVisibilityMode="allowlist", allowFrom=["+8613800138000"]
)
assert policy.evaluate_context_visibility(
"iMessage;+;chat001", "+8613900139000"
) is False
def test_require_mention_property(self):
policy = self.make_policy(requireMention=True)
assert policy.require_mention is True
def test_should_update_last_route(self):
policy = self.make_policy(allowFrom=["+8613800138000"])
assert policy.should_update_last_route("+8613800138000") is True
def test_should_update_last_route_no_owner(self):
policy = self.make_policy(allowFrom=[])
assert policy.should_update_last_route("+8613800138000") is True