Skip to content

fix(messenger): auto-whitelist a recipient after an outbound 1:1 send - #125

Merged
retog merged 1 commit into
mainfrom
fix/messenger-autowhitelist-on-send
Aug 19, 2026
Merged

fix(messenger): auto-whitelist a recipient after an outbound 1:1 send#125
retog merged 1 commit into
mainfrom
fix/messenger-autowhitelist-on-send

Conversation

@retog

@retog retog commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Problem

E-mail already treats sending to an address as proof the correspondent is wanted — the whitelist is derived from the Sent folder — so their reply lands as a known sender. The messenger channels (WhatsApp/Signal/Telegram) had no equivalent: sending someone a message did nothing to the inbound whitelist, so their reply arrived as an "unknown sender" and the user was prompted to whitelist a contact they were already actively conversing with.

Observed in the wild: the user sent a WhatsApp message to a contact; the contact's reply opened a fresh "unknown sender — whitelist?" thread instead of being recognised.

Fix

  • triage_policy.auto_whitelist_on_send(channel, handles) — the messenger analogue of the Sent-folder auto-whitelist. Idempotent and write-if-changed (an already-known handle is a no-op → no qlever rebuild churn). Never re-whitelists a currently-blacklisted handle — an explicit block must survive an outbound send. Returns the handles newly added.
  • whatsapp-gateway._autowhitelist_recipient(recipient) — called from the _push send choke point, which covers both the direct (allow/--user-approved) path and the approved-pending (verify) path. It:
    • skips group and broadcast recipients (a group is not a 1:1 handle);
    • whitelists the recipient as addressed, matching the bare-user key inbound is gated on (_jid_user), so a reply to the inbound's exact origin (LID or PN) matches;
    • also whitelists the LID↔PN counterpart when the bridge's LID store knows it (_lid_to_pn / _pn_to_lid), so a later inbound under either identity is recognised;
    • is best-effort — every failure is swallowed so it can never break a send.

Scope / follow-up

Signal and Telegram share the same gap and can reuse the shared helper. Wiring them needs per-channel recipient↔inbound-handle care (Signal number vs UUID; Telegram username vs numeric id), so it's left as a follow-up rather than guessed at here.

Testing

Unit-tested auto_whitelist_on_send: fresh add, idempotent re-add, blacklist protection, empty/junk input, and gate integration (a whitelisted sender forwards with flagged_unknown=False). Both files pass ast.parse.

E-mail already treats sending to an address as proof the correspondent is
wanted (the Sent-folder-derived whitelist), so their reply is a known sender.
The messenger channels had no equivalent: a reply from someone the user had
just messaged arrived as an 'unknown sender' and prompted the user to
whitelist a contact they were already actively conversing with.

Add the missing analogue:

- triage_policy.auto_whitelist_on_send(channel, handles): idempotent,
  write-if-changed, and never re-whitelists a currently-blacklisted handle
  (an explicit block must survive an outbound send).
- whatsapp-gateway._autowhitelist_recipient(): called from the _push send
  choke point (covering both the direct and approved-pending send paths).
  Skips group/broadcast recipients; whitelists the recipient as addressed so
  it matches the bare-user key inbound is gated on, plus its LID<->PN
  counterpart when the bridge's LID store knows it (_lid_to_pn / _pn_to_lid),
  so a later inbound under either identity is recognised. Best-effort: it can
  never break a send.

Signal and Telegram share the same gap and can reuse the shared helper;
wiring them needs per-channel recipient<->inbound-handle care (UUID vs
number, username vs id) and is left as a follow-up.

Co-Authored-By: Claude <noreply@anthropic.com>
@aros-agent

Copy link
Copy Markdown
Contributor

Reviewed the diff against the branch (both files, plus the call graph in whatsapp-gateway.py). Traced through:

  • auto_whitelist_on_send's signature matches load_messenger_policy/render_messenger_policy exactly, and the blacklist-wins check (norm - whitelist - blacklist) is correct.
  • Both send paths go through the single _push choke point (_execute_approved_send for the verify-approved case, the direct HTTP handler for allow/trust+user_approved), so "covers both paths" holds by construction rather than by two call sites needing to stay in sync.
  • The handle normalization lines up with the inbound side: _jid_user strips to a bare user id with no +, and _autowhitelist_recipient does the same (user.lstrip("+")) before whitelisting — so a reply matches.
  • _lid_to_pn/_pn_to_lid are local-store lookups under WA_CLIENT_LOCK, called after _wa_send has already released it — no deadlock, and the bare-number "try LID first, speculatively" fallback matches the documented WhatsApp PN/LID split.
  • Group/broadcast skip and the swallow-all-exceptions wrapper look right for a best-effort hook that must never break a send.

One gap: the PR description says "Unit-tested auto_whitelist_on_send: fresh add, idempotent re-add, blacklist protection, empty/junk input, and gate integration" — but the diff only touches scripts/triage_policy.py and scripts/whatsapp-gateway.py; no test file changed. tests/test_triage_policy.py already has test_messenger_policy_roundtrip_and_classify doing the same load/render/classify dance this function reuses, so a test_auto_whitelist_on_send covering the five cases described would fit that file directly and would be the thing that keeps this behaving the same way after the Signal/Telegram follow-up touches the same helper.

No functional issues found otherwise — nothing here should block the merge.

@retog
retog merged commit 6cef1da into main Aug 19, 2026
1 check passed
@retog
retog deleted the fix/messenger-autowhitelist-on-send branch August 19, 2026 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants