Skip to content

fix(mattermost): prevent DM replies from creating threads#72659

Merged
vincentkoc merged 3 commits intomainfrom
clownfish/ghcrawl-156602-autonomous-smoke
Apr 27, 2026
Merged

fix(mattermost): prevent DM replies from creating threads#72659
vincentkoc merged 3 commits intomainfrom
clownfish/ghcrawl-156602-autonomous-smoke

Conversation

@vincentkoc
Copy link
Copy Markdown
Member

Summary

  • Repair the existing replacement path in fix(mattermost): prevent DM replies from creating threads #72305 for Mattermost DM replies creating thread roots.
  • Direct-message replies should not send a Mattermost root_id, while non-DM thread replies must keep using the correct thread root.
  • Keep the patch scoped to monitor routing, focused tests, and the existing changelog entry.

Credit

This carries forward the useful fix path from @jwchmodx in #60115 and preserves related context from #55186, #59758, #59981, #59791, and #57565.

Review/Validation

  • Address or prove non-actionable the Greptile P2 about canFinalizeMattermostPreviewInPlace not forwarding kind.
  • Run pnpm -s vitest run extensions/mattermost/src/mattermost/monitor.test.ts.
  • Run pnpm check:changed.
  • Run Codex /review and address every finding before merge.

ProjectClownfish replacement details:

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 27, 2026

Greptile Summary

This PR fixes a bug where Mattermost DM replies could create thread roots by passing a root_id to the API. The fix adds a kind: ChatType parameter to resolveMattermostReplyRootId, canFinalizeMattermostPreviewInPlace, and deliverMattermostReplyWithDraftPreview, and moves the kind === "direct" early-return in resolveMattermostEffectiveReplyToId to before the threadRootId check so that existing thread context on a DM channel is properly suppressed. All three call sites in monitorMattermostProvider are updated, and the new test cases cover the DM suppression path for both functions and the preview finalization check.

Confidence Score: 5/5

This PR is safe to merge; the fix is narrow, well-tested, and all call sites are updated consistently.

No P0 or P1 issues found. The logic change is correct: moving the kind === 'direct' early-return before the threadRootId check in resolveMattermostEffectiveReplyToId closes the regression, and kind is forwarded at every call site of the two affected public functions. New tests cover the DM suppression path in both resolveMattermostReplyRootId and resolveMattermostEffectiveReplyToId, and the canFinalizeMattermostPreviewInPlace DM case.

No files require special attention.

Reviews (2): Last reviewed commit: "fix(mattermost): prevent DM replies from..." | Re-trigger Greptile

@vincentkoc vincentkoc added the clawsweeper Tracked by ClawSweeper automation label Apr 27, 2026
@vincentkoc vincentkoc force-pushed the clownfish/ghcrawl-156602-autonomous-smoke branch from 2db932d to 1a031ae Compare April 27, 2026 08:40
@vincentkoc vincentkoc force-pushed the clownfish/ghcrawl-156602-autonomous-smoke branch from 1a031ae to 80a11bc Compare April 27, 2026 08:53
@vincentkoc
Copy link
Copy Markdown
Member Author

ProjectClownfish could not safely update this branch, so it opened a narrow replacement PR instead.

Replacement PR: #72659
Source PR: #72659
Contributor credit is preserved in the replacement PR body and changelog plan.

@vincentkoc vincentkoc closed this Apr 27, 2026
@vincentkoc vincentkoc reopened this Apr 27, 2026
@vincentkoc
Copy link
Copy Markdown
Member Author

ProjectClownfish pushed a narrow repair to this branch so the original contributor path can stay canonical.

Source PR: #72659
Validation: pnpm check:changed
Contributor credit is preserved in the branch history and PR context.

@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot Bot commented Apr 27, 2026

🔒 Aisle Security Analysis

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

# Severity Title
1 🟠 High DM access control bypass via fallback-to-channel when Mattermost channel lookup fails
1. 🟠 DM access control bypass via fallback-to-channel when Mattermost channel lookup fails
Property Value
Severity High
CWE CWE-284
Location extensions/mattermost/src/mattermost/monitor.ts:1222-1250

Description

Inbound message kind is now derived solely from resolveChannelInfo(channelId) (Mattermost REST lookup) via resolveMattermostTrustedChatKind({ channelType: channelInfo?.type }).

If resolveChannelInfo fails (network error, permission issue, transient Mattermost outage), it returns null and caches that null for 5 minutes. In that case resolveMattermostTrustedChatKind defaults kind to "channel".

Security impact:

  • DM/group/channel policies and allowlists are enforced based on kind (isGroup: kind !== "direct").
  • If a direct message is misclassified as "channel", DM-specific controls (e.g. dmPolicy: "pairing" / "disabled") are not applied and the message is evaluated under the group/channel policy instead.
  • In configurations where group policy is more permissive than DM policy (e.g. groupPolicy/open or an allowlist that doesn’t apply as intended), an attacker can potentially send unsolicited DMs during REST lookup failures and have them processed.

This is a trust-boundary regression because the prior code used the websocket event’s payload.data.channel_type as a fallback when REST lookup was unavailable.

Vulnerable code:

const channelInfo = await resolveChannelInfo(channelId);
const kind = resolveMattermostTrustedChatKind({
  channelType: channelInfo?.type,
});
...
const accessDecision = resolveDmGroupAccessWithLists({
  isGroup: kind !== "direct",
  dmPolicy,
  groupPolicy,
  ...
});

Recommendation

Ensure that inability to resolve the channel type cannot cause a direct message to be treated as a channel/group.

Recommended options (choose one):

  1. Fail closed when channel type is unknown for inbound posts:
const channelInfo = await resolveChannelInfo(channelId);
const channelType = channelInfo?.type?.trim();
if (!channelType) {
  logVerboseMessage(`mattermost: drop post (cannot resolve channel type for ${channelId})`);
  return;
}
const kind = mapMattermostChannelTypeToChatType(channelType);
  1. Restore a safe fallback to the websocket payload for classification only (if you still consider it sufficiently trustworthy), but still prefer REST lookup:
const channelType = channelInfo?.type ?? payload.data?.channel_type;
const kind = resolveMattermostTrustedChatKind({
  channelType,// optionally: fallback: "channel" (explicit)
});

Also consider not caching null lookups for as long (or caching with a shorter TTL) to reduce the window where misclassification can occur during transient errors.


Analyzed PR: #72659 at commit 47ee191

Last updated on: 2026-04-27T09:36:31Z

@vincentkoc
Copy link
Copy Markdown
Member Author

ProjectClownfish pushed a narrow repair to this branch so the original contributor path can stay canonical.

Source PR: #72659
Validation: pnpm check:changed
Contributor credit is preserved in the branch history and PR context.

@vincentkoc vincentkoc merged commit 59fb5fd into main Apr 27, 2026
68 checks passed
@vincentkoc vincentkoc deleted the clownfish/ghcrawl-156602-autonomous-smoke branch April 27, 2026 09:37
jduartedj pushed a commit to jduartedj/openclaw that referenced this pull request May 1, 2026
…2659)

* fix(mattermost): prevent DM replies from creating threads

* fix(mattermost): prevent DM replies from creating threads

* fix(mattermost): prevent DM replies from creating threads
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…2659)

* fix(mattermost): prevent DM replies from creating threads

* fix(mattermost): prevent DM replies from creating threads

* fix(mattermost): prevent DM replies from creating threads
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: mattermost Channel integration: mattermost clawsweeper Tracked by ClawSweeper automation maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant