diff --git a/nanobot/channels/signal/runtime.py b/nanobot/channels/signal/runtime.py index 253f8136e..c3358e44e 100644 --- a/nanobot/channels/signal/runtime.py +++ b/nanobot/channels/signal/runtime.py @@ -862,6 +862,7 @@ class SignalChannel(BaseChannel): return False, chat_id if ( self.config.group.policy == "allowlist" + and "*" not in self.config.group.allow_from and chat_id not in self.config.group.allow_from ): self.logger.info( @@ -1061,6 +1062,9 @@ class SignalChannel(BaseChannel): def _sender_matches_allowlist(cls, sender_id: str, allow_list: list[str]) -> bool: """Return True if any normalized variant of sender_id is on allow_list. + A ``"*"`` entry allows every sender, matching the channel-wide + allowlist contract. + Both ``sender_id`` and each allow_list entry can be a single identifier or a pipe-joined composite of several (e.g. ``"+1234567890|uuid-abc"``); both sides are split on ``|`` and each @@ -1070,6 +1074,8 @@ class SignalChannel(BaseChannel): """ if not allow_list: return False + if "*" in allow_list: + return True sender_variants: set[str] = set() for part in str(sender_id).split("|"): sender_variants.update(cls._normalize_signal_id(part)) diff --git a/nanobot/channels/signal/tests/test_signal_channel.py b/nanobot/channels/signal/tests/test_signal_channel.py index a842d2dbf..ff9ddcacf 100644 --- a/nanobot/channels/signal/tests/test_signal_channel.py +++ b/nanobot/channels/signal/tests/test_signal_channel.py @@ -790,6 +790,14 @@ class TestHandleDataMessageDM: await ch._handle_receive_notification(params) assert len(handled) == 1 + @pytest.mark.asyncio + async def test_dm_allowlist_wildcard_preserves_content(self): + ch, handled = self._make_dm_channel(policy="allowlist", allow_from=["*"]) + params = _dm_envelope(source_number="+19995550001", message="wildcard DM") + await ch._handle_receive_notification(params) + assert len(handled) == 1 + assert handled[0]["content"] == "wildcard DM" + @pytest.mark.asyncio async def test_dm_allowlist_rejected_triggers_pairing(self): # Denied DM senders go through super()._handle_message which checks @@ -1025,6 +1033,16 @@ class TestHandleDataMessageGroup: await ch._handle_receive_notification(params) assert len(handled) == 1 + @pytest.mark.asyncio + async def test_group_allowlist_wildcard_preserves_content(self): + ch, handled = self._make_group_channel( + policy="allowlist", allow_from=["*"], require_mention=False + ) + params = _group_envelope(group_id="grp==", source_name="Alice", message="wildcard group") + await ch._handle_receive_notification(params) + assert len(handled) == 1 + assert "[Alice]: wildcard group" in handled[0]["content"] + @pytest.mark.asyncio async def test_group_allowlist_rejected(self): ch, handled = self._make_group_channel(policy="allowlist", allow_from=["other=="])