mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-05 02:31:47 +00:00
fix(signal): skip reactions for unauthorized senders
The on_processing_start hook fired a reaction emoji (👀) on every
inbound Signal message before run.py's _is_user_authorized check.
This meant contacts not in SIGNAL_ALLOWED_USERS would see the bot
react to their messages even though Hermes silently dropped them —
leaking the presence of the bot and causing confusing UX.
Two changes to gateway/platforms/signal.py:
1. Read SIGNAL_ALLOWED_USERS into self.dm_allow_from in __init__
(mirrors the group_allow_from pattern already in place).
2. Add _reactions_enabled(event) — two-gate check:
- SIGNAL_REACTIONS=false/0/no disables reactions globally
- If SIGNAL_ALLOWED_USERS is set, only react to senders in
the allowlist (skips unauthorized contacts)
Both on_processing_start and on_processing_complete now call this
guard before sending any reaction.
Telegram already has an equivalent _reactions_enabled() guard
(controlled by TELEGRAM_REACTIONS). This brings Signal to parity.
This commit is contained in:
parent
e89376d66f
commit
8a4fe80f8d
1 changed files with 31 additions and 0 deletions
|
|
@ -192,6 +192,15 @@ class SignalAdapter(BasePlatformAdapter):
|
|||
group_allowed_str = os.getenv("SIGNAL_GROUP_ALLOWED_USERS", "")
|
||||
self.group_allow_from = set(_parse_comma_list(group_allowed_str))
|
||||
|
||||
# DM allowlist — mirrors SIGNAL_ALLOWED_USERS checked by run.py.
|
||||
# Stored here so the reaction hooks can skip unauthorized senders
|
||||
# (reactions fire before run.py's auth gate, so without this check
|
||||
# every inbound DM from any contact gets a 👀 reaction).
|
||||
# "*" means all users allowed (open mode); empty means no restriction
|
||||
# recorded at adapter level (run.py still enforces auth separately).
|
||||
dm_allowed_str = os.getenv("SIGNAL_ALLOWED_USERS", "*")
|
||||
self.dm_allow_from = set(_parse_comma_list(dm_allowed_str))
|
||||
|
||||
# HTTP client
|
||||
self.client: Optional[httpx.AsyncClient] = None
|
||||
|
||||
|
|
@ -1430,8 +1439,28 @@ class SignalAdapter(BasePlatformAdapter):
|
|||
return None
|
||||
return (author, ts)
|
||||
|
||||
def _reactions_enabled(self, event: "MessageEvent" = None) -> bool:
|
||||
"""Check if message reactions are enabled for this event.
|
||||
|
||||
Two gates:
|
||||
1. SIGNAL_REACTIONS env var — set to false/0/no to disable globally.
|
||||
2. DM allowlist — if SIGNAL_ALLOWED_USERS is set, only react to
|
||||
messages from senders in that list. This prevents unauthorized
|
||||
contacts from seeing the 👀 reaction (which fires before run.py's
|
||||
auth gate and would otherwise reveal that a bot is listening).
|
||||
"""
|
||||
if os.getenv("SIGNAL_REACTIONS", "true").lower() in ("false", "0", "no"):
|
||||
return False
|
||||
if event is not None:
|
||||
sender = getattr(getattr(event, "source", None), "user_id", None)
|
||||
if sender and "*" not in self.dm_allow_from and sender not in self.dm_allow_from:
|
||||
return False
|
||||
return True
|
||||
|
||||
async def on_processing_start(self, event: MessageEvent) -> None:
|
||||
"""React with 👀 when processing begins."""
|
||||
if not self._reactions_enabled(event):
|
||||
return
|
||||
target = self._extract_reaction_target(event)
|
||||
if target:
|
||||
await self.send_reaction(event.source.chat_id, "👀", *target)
|
||||
|
|
@ -1442,6 +1471,8 @@ class SignalAdapter(BasePlatformAdapter):
|
|||
On CANCELLED we leave the 👀 in place — no terminal outcome means
|
||||
the reaction should keep reflecting "in progress" (matches Telegram).
|
||||
"""
|
||||
if not self._reactions_enabled(event):
|
||||
return
|
||||
if outcome == ProcessingOutcome.CANCELLED:
|
||||
return
|
||||
target = self._extract_reaction_target(event)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue