Skip to content

fix(imessage): avoid DM self-chat false positives#61619

Merged
obviyus merged 4 commits into
openclaw:mainfrom
neeravmakwana:fix/imessage-dm-self-chat-61543
Apr 9, 2026
Merged

fix(imessage): avoid DM self-chat false positives#61619
obviyus merged 4 commits into
openclaw:mainfrom
neeravmakwana:fix/imessage-dm-self-chat-61543

Conversation

@neeravmakwana

Copy link
Copy Markdown
Contributor

Summary

  • Problem: iMessage DM outbound rows can be misclassified as self-chat when imsg reports the peer handle in both sender and chat_identifier.
  • Why it matters: that sends normal DMs down the self-chat echo path, so a cache miss can leak the bot's own outbound reply back into inbound handling and start a loop.
  • What changed: the iMessage monitor now uses destination_caller_id when available to distinguish true self-chat from DM false positives, and it preserves that metadata in the parsed payload.
  • What did NOT change (scope boundary): no echo-cache redesign, no config changes, and no behavior change for older payloads that do not include destination_caller_id.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause (if applicable)

  • Root cause: resolveIMessageInboundDecision() treated sender === chat_identifier as sufficient proof of self-chat, but current imsg payloads can use the peer handle for both fields in a normal DM outbound row.
  • Missing detection / guardrail: the monitor ignored destination_caller_id, which is the field that differentiates a real self-chat from this DM false positive.
  • Contributing context (if known): the earlier self-chat fixes addressed reflected duplicate rows, but they still depended on the older heuristic for identifying self-chat in the first place.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: extensions/imessage/src/monitor/self-chat-dedupe.test.ts, extensions/imessage/src/monitor.gating.test.ts
  • Scenario the test should lock in: is_from_me=true DM rows with sender===chat_identifier but a different destination_caller_id must still take the normal from me drop path, while real self-chat still dispatches.
  • Why this is the smallest reliable guardrail: the bug lives entirely inside inbound payload parsing and the inbound-decision branch, so focused tests on those two seams cover the real regression surface.
  • Existing test that already covers this (if any): the existing self-chat tests covered the legacy heuristic and true self-chat behavior, but not the DM false-positive shape.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

  • OpenClaw no longer treats normal iMessage DM outbound rows as self-chat when imsg provides a different destination_caller_id.
  • That keeps those rows on the normal from me drop path and avoids self-echo loops caused by cache misses.

Diagram (if applicable)

Before:
[normal DM outbound row]
  -> sender == chat_identifier
  -> misclassified as self-chat
  -> echo cache miss
  -> dispatched as inbound

After:
[normal DM outbound row]
  -> destination_caller_id != sender
  -> classified as normal DM
  -> dropped as from me

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS 15.3 / Darwin 25.3.0
  • Runtime/container: Node.js via repo scripts
  • Model/provider: N/A
  • Integration/channel: iMessage via imsg
  • Relevant config (redacted): standard iMessage channel config; no config changes required

Steps

  1. Receive a normal iMessage DM where imsg reports an outbound row with the peer handle in both sender and chat_identifier.
  2. Let OpenClaw process the is_from_me=true row.
  3. Observe whether it takes the self-chat path or the normal from me drop path.

Expected

  • The row is recognized as a normal DM outbound event and dropped as from me.
  • Real self-chat rows still use the self-chat path.

Actual

  • Before this change, the row could be misclassified as self-chat and leak through on an echo-cache miss.
  • After this change, the same shape drops as from me when destination_caller_id points at a different local handle.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios: ran a direct node --import tsx repro against resolveIMessageInboundDecision() for the reported DM false-positive payload shape and for the true self-chat shape; the DM case now drops as from me and the true self-chat case still dispatches.
  • Edge cases checked: parser now preserves destination_caller_id; current fallback behavior remains unchanged when that field is absent.
  • What you did not verify: the heavier Vitest lanes (pnpm test, pnpm test:extension imessage, and a focused Vitest invocation) stalled after Vitest startup in this environment, so I did not get a completed test summary from those commands.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No
  • If yes, exact upgrade steps:

Risks and Mitigations

  • Risk: older imsg payloads that omit destination_caller_id still rely on the legacy heuristic.

    • Mitigation: the new logic is additive and only tightens classification when the stronger signal is present, so older payload behavior is preserved rather than reinterpreted.
  • Risk: this assumes destination_caller_id is the local sending identity for the normal DM false-positive shape.

    • Mitigation: the new regression test locks in the exact reported payload shape, and the change is scoped to is_from_me self-chat classification only.

AI Disclosure

This PR was prepared with AI assistance and reviewed before submission.

Made with Cursor

@greptile-apps

greptile-apps Bot commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes iMessage DM self-chat false positives by using destination_caller_id to distinguish true self-chat from normal DM outbound rows. When destination_caller_id is present and differs from sender, the message is correctly classified as a normal DM and dropped as from me rather than entering the self-chat echo path. Older payloads without this field preserve the existing sender === chat_identifier heuristic.

Confidence Score: 5/5

Safe to merge — additive fix with no breaking changes, backward-compatible fallback, and targeted test coverage for both the false-positive and true self-chat shapes.

No P0 or P1 issues. The new isSelfChat condition correctly handles all three cases (true self-chat, DM false-positive, legacy payload without destination_caller_id), the type/parser/decision layers are aligned, and two new tests lock in the regression surface.

No files require special attention.

Reviews (1): Last reviewed commit: "fix(imessage): avoid DM self-chat false ..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4d73fe8682

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread extensions/imessage/src/monitor/inbound-processing.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 10fedad565

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread extensions/imessage/src/monitor/inbound-processing.ts Outdated
@obviyus obviyus self-assigned this Apr 9, 2026
@obviyus
obviyus force-pushed the fix/imessage-dm-self-chat-61543 branch from 60c12d5 to 322e6a8 Compare April 9, 2026 11:42
@aisle-research-bot

aisle-research-bot Bot commented Apr 9, 2026

Copy link
Copy Markdown

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🟡 Medium Unbounded participant list normalization can cause CPU/memory DoS in self-chat detection
1. 🟡 Unbounded participant list normalization can cause CPU/memory DoS in self-chat detection
Property Value
Severity Medium
CWE CWE-400
Location extensions/imessage/src/monitor/inbound-processing.ts:207-214

Description

resolveIMessageInboundDecision now always normalizes every entry in message.participants and stores them in a Set to evaluate matchesSelfChatDestination.

Because participants comes from the parsed notification payload and is only type-checked (array of strings) without any size/length limits, a malicious or malformed notification containing:

  • a very large participants array, and/or
  • extremely long participant strings

can trigger excessive allocations and CPU work (mapping + normalization + Set construction) on every inbound message, leading to denial-of-service of the monitor process.

Vulnerable code:

const chatParticipantHandles = new Set(
  (params.message.participants ?? [])
    .map((participant) => normalizeIMessageHandle(participant))
    .filter((participant): participant is string => participant.length > 0),
);

Recommendation

Add defensive limits before processing untrusted arrays/strings, and avoid building the Set unless it’s actually needed.

Example:

const MAX_PARTICIPANTS = 64;
const MAX_HANDLE_LEN = 256;

const rawParticipants = Array.isArray(params.message.participants)
  ? params.message.participants.slice(0, MAX_PARTICIPANTS)
  : [];

const chatParticipantHandles = new Set(
  rawParticipants
    .map((p) => (typeof p === "string" ? p.slice(0, MAX_HANDLE_LEN) : ""))
    .map((p) => normalizeIMessageHandle(p))
    .filter((p): p is string => p.length > 0),
);

Also consider only constructing chatParticipantHandles inside the isSelfChat path (or when destinationCallerIdNormalized is non-null and doesn’t equal senderNormalized) to reduce work for the common case.


Analyzed PR: #61619 at commit 322e6a8

Last updated on: 2026-04-09T11:44:18Z

@obviyus obviyus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed latest changes; landing now.

@obviyus
obviyus merged commit 9267c3f into openclaw:main Apr 9, 2026
8 checks passed
@obviyus

obviyus commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Landed on main.

Thanks @neeravmakwana.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 322e6a89eb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +216 to +218
destinationCallerIdNormalized == null ||
destinationCallerIdNormalized === senderNormalized ||
chatParticipantHandles.has(destinationCallerIdNormalized);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exclude DM participant lists from self-chat classification

matchesSelfChatDestination currently treats destination_caller_id appearing in participants as proof of self-chat, but that also matches normal 1:1 DM payloads where participants contains both the peer and the local handle. In that shape, outbound is_from_me=true rows with sender===chat_identifier are still classified as self-chat and can be dispatched on an echo-cache miss, reintroducing the DM self-echo/loop path this change is trying to prevent. This severity applies when imsg emits full participant arrays for those DM rows.

Useful? React with 👍 / 👎.

steipete pushed a commit that referenced this pull request Apr 10, 2026
)

* fix(imessage): avoid DM self-chat false positives

* fix(imessage): treat blank destination caller id as missing

* fix(imessage): preserve alias self-chat

* fix: preserve iMessage self-chat aliases (#61619) (thanks @neeravmakwana)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
…eravmakwana)

* fix(imessage): avoid DM self-chat false positives

* fix(imessage): treat blank destination caller id as missing

* fix(imessage): preserve alias self-chat

* fix: preserve iMessage self-chat aliases (openclaw#61619) (thanks @neeravmakwana)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…eravmakwana)

* fix(imessage): avoid DM self-chat false positives

* fix(imessage): treat blank destination caller id as missing

* fix(imessage): preserve alias self-chat

* fix: preserve iMessage self-chat aliases (openclaw#61619) (thanks @neeravmakwana)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…eravmakwana)

* fix(imessage): avoid DM self-chat false positives

* fix(imessage): treat blank destination caller id as missing

* fix(imessage): preserve alias self-chat

* fix: preserve iMessage self-chat aliases (openclaw#61619) (thanks @neeravmakwana)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
…eravmakwana)

* fix(imessage): avoid DM self-chat false positives

* fix(imessage): treat blank destination caller id as missing

* fix(imessage): preserve alias self-chat

* fix: preserve iMessage self-chat aliases (openclaw#61619) (thanks @neeravmakwana)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
…eravmakwana)

* fix(imessage): avoid DM self-chat false positives

* fix(imessage): treat blank destination caller id as missing

* fix(imessage): preserve alias self-chat

* fix: preserve iMessage self-chat aliases (openclaw#61619) (thanks @neeravmakwana)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: imessage Channel integration: imessage size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

iMessage plugin: self-echo infinite loop in DM chats (isSelfChat false positive)

2 participants