fix(matrix): pass agentId to buildMentionRegexes for agent-level mention patterns#51106
fix(matrix): pass agentId to buildMentionRegexes for agent-level mention patterns#51106mvanhorn wants to merge 3 commits into
Conversation
🔒 Aisle Security AnalysisWe found 1 potential security issue(s) in this PR:
1. 🟠 Mention-required Matrix room messages processed before mention drop (DoS via poll/media/thread resolution)
DescriptionIn Impact:
This creates a denial-of-service vector (CPU/network/IO amplification) precisely in the pathway meant to reduce processing for unmentioned room chatter. Vulnerable logic (defer drop + drop only after route resolution): // ...
if (isRoom && shouldRequireMention && !wasMentioned && !shouldBypassMention && !mentionPrecheckText) {
logger.info("skipping room message", { roomId, reason: "no-mention" });
return;
}
const mentionDropDeferred =
isRoom && shouldRequireMention && !wasMentioned && !shouldBypassMention;
// ... expensive work occurs here (poll/media/thread resolution) ...
const { route } = resolveMatrixInboundRoute({ /* ... */ });
if (mentionDropDeferred) {
// ... re-resolve mentions using agent-level patterns ...
if (!wasMentioned) {
logger.info("skipping room message", { roomId, reason: "no-mention" });
return;
}
}RecommendationPerform route resolution + agent-level mention re-check before any expensive processing, and return early if the message is still unmentioned. One safe restructuring pattern: // after initial resolveMentions + shouldRequireMention/shouldBypassMention computation
const needsAgentMentionCheck =
isRoom && shouldRequireMention && !wasMentioned && !shouldBypassMention;
if (needsAgentMentionCheck) {
// Resolve route early (cheap) so we can build agent-specific mention patterns
const { route: preRoute } = resolveMatrixInboundRoute({
cfg,
accountId,
roomId,
senderId,
isDirectMessage,
messageId,
threadRootId,
eventTs: eventTs ?? undefined,
resolveAgentRoute: core.channel.routing.resolveAgentRoute,
});
const agentMentionRegexes = core.channel.mentions.buildMentionRegexes(cfg, preRoute.agentId);
const agentMentionResult = resolveMentions({
content,
userId: selfUserId,
text: mentionPrecheckText,
mentionRegexes: agentMentionRegexes,
});
if (!agentMentionResult.wasMentioned) {
logger.info("skipping room message", { roomId, reason: "no-mention" });
return; // BEFORE poll snapshot, media download, thread lookups, etc.
}
// carry forward route/agentId from preRoute to avoid resolving twice
}
// only now do fetchMatrixPollSnapshot / downloadMatrixMedia / resolveThreadContextAdditionally consider:
Analyzed PR: #51106 at commit Last updated on: 2026-03-20T16:34:03Z |
Greptile SummaryThis PR fixes a bug (#51082) where the Matrix plugin's mention-required filter was dropping all messages in rooms with The fix defers the mention-required drop to after Key changes:
Notable concern: Confidence Score: 3/5
Prompt To Fix All With AIThis is a comment left during a code review.
Path: extensions/matrix/src/matrix/monitor/handler.ts
Line: 556
Comment:
**`canDetectMention` not updated after agent-level pattern resolution**
`canDetectMention` is computed here using only channel-level `mentionRegexes` and `hasExplicitMention`. When the deferred agent-level check later sets `wasMentioned = true` (via `agentMentionRegexes`), `canDetectMention` remains `false` if the channel-level regexes were empty and no explicit Matrix `m.mentions` was present.
This value is then passed to `shouldAckReaction` at line 804:
```ts
canDetectMention,
effectiveWasMentioned: wasMentioned || shouldBypassMention, // may now be true
```
This creates an inconsistency: the bot detected a mention (`wasMentioned = true`) but `canDetectMention = false`. Depending on `shouldAckReaction`'s internal logic, ack reactions could be silently suppressed for messages that match only agent-level patterns.
Consider updating `canDetectMention` after the deferred check resolves:
```ts
// after the deferred block
const canDetectMention =
mentionRegexes.length > 0 || hasExplicitMention || wasMentioned;
```
Or, re-evaluate it before it is consumed by `shouldAckReaction`.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/matrix/src/matrix/monitor/handler.test.ts
Line: 476-479
Comment:
**Removed assertions were still valid for media-only messages**
The assertions `expect(getMemberDisplayName).not.toHaveBeenCalled()`, `expect(getRoomInfo).not.toHaveBeenCalled()`, and `expect(resolveAgentRoute).not.toHaveBeenCalled()` were removed from the "skips media downloads for unmentioned group media messages" test.
For media-only events (`body: ""`), `mentionPrecheckText` is empty. The new early-drop guard:
```ts
if (isRoom && shouldRequireMention && !wasMentioned && !shouldBypassMention && !mentionPrecheckText) {
return; // fires for empty body
}
```
still fires before `getSenderName()`, `getRoomInfo()`, and `resolveMatrixInboundRoute()` are reached (lines 647, 648, 664). The three removed assertions should still pass and serve as useful guardrails confirming the early-exit optimization is preserved for media-only messages.
Compare this with the poll-response test at line 481 which still retains all three equivalent assertions — it would be consistent to keep them here too.
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: "fix(matrix): pass ag..." |
| @@ -554,10 +554,21 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam | |||
| commandAuthorized && | |||
| hasControlCommandInMessage; | |||
| const canDetectMention = mentionRegexes.length > 0 || hasExplicitMention; | |||
There was a problem hiding this comment.
canDetectMention not updated after agent-level pattern resolution
canDetectMention is computed here using only channel-level mentionRegexes and hasExplicitMention. When the deferred agent-level check later sets wasMentioned = true (via agentMentionRegexes), canDetectMention remains false if the channel-level regexes were empty and no explicit Matrix m.mentions was present.
This value is then passed to shouldAckReaction at line 804:
canDetectMention,
effectiveWasMentioned: wasMentioned || shouldBypassMention, // may now be trueThis creates an inconsistency: the bot detected a mention (wasMentioned = true) but canDetectMention = false. Depending on shouldAckReaction's internal logic, ack reactions could be silently suppressed for messages that match only agent-level patterns.
Consider updating canDetectMention after the deferred check resolves:
// after the deferred block
const canDetectMention =
mentionRegexes.length > 0 || hasExplicitMention || wasMentioned;Or, re-evaluate it before it is consumed by shouldAckReaction.
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/matrix/src/matrix/monitor/handler.ts
Line: 556
Comment:
**`canDetectMention` not updated after agent-level pattern resolution**
`canDetectMention` is computed here using only channel-level `mentionRegexes` and `hasExplicitMention`. When the deferred agent-level check later sets `wasMentioned = true` (via `agentMentionRegexes`), `canDetectMention` remains `false` if the channel-level regexes were empty and no explicit Matrix `m.mentions` was present.
This value is then passed to `shouldAckReaction` at line 804:
```ts
canDetectMention,
effectiveWasMentioned: wasMentioned || shouldBypassMention, // may now be true
```
This creates an inconsistency: the bot detected a mention (`wasMentioned = true`) but `canDetectMention = false`. Depending on `shouldAckReaction`'s internal logic, ack reactions could be silently suppressed for messages that match only agent-level patterns.
Consider updating `canDetectMention` after the deferred check resolves:
```ts
// after the deferred block
const canDetectMention =
mentionRegexes.length > 0 || hasExplicitMention || wasMentioned;
```
Or, re-evaluate it before it is consumed by `shouldAckReaction`.
How can I resolve this? If you propose a fix, please make it concise.| } as MatrixRawEvent); | ||
|
|
||
| expect(downloadContent).not.toHaveBeenCalled(); | ||
| expect(getMemberDisplayName).not.toHaveBeenCalled(); | ||
| expect(getRoomInfo).not.toHaveBeenCalled(); | ||
| expect(resolveAgentRoute).not.toHaveBeenCalled(); | ||
| }); |
There was a problem hiding this comment.
Removed assertions were still valid for media-only messages
The assertions expect(getMemberDisplayName).not.toHaveBeenCalled(), expect(getRoomInfo).not.toHaveBeenCalled(), and expect(resolveAgentRoute).not.toHaveBeenCalled() were removed from the "skips media downloads for unmentioned group media messages" test.
For media-only events (body: ""), mentionPrecheckText is empty. The new early-drop guard:
if (isRoom && shouldRequireMention && !wasMentioned && !shouldBypassMention && !mentionPrecheckText) {
return; // fires for empty body
}still fires before getSenderName(), getRoomInfo(), and resolveMatrixInboundRoute() are reached (lines 647, 648, 664). The three removed assertions should still pass and serve as useful guardrails confirming the early-exit optimization is preserved for media-only messages.
Compare this with the poll-response test at line 481 which still retains all three equivalent assertions — it would be consistent to keep them here too.
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/matrix/src/matrix/monitor/handler.test.ts
Line: 476-479
Comment:
**Removed assertions were still valid for media-only messages**
The assertions `expect(getMemberDisplayName).not.toHaveBeenCalled()`, `expect(getRoomInfo).not.toHaveBeenCalled()`, and `expect(resolveAgentRoute).not.toHaveBeenCalled()` were removed from the "skips media downloads for unmentioned group media messages" test.
For media-only events (`body: ""`), `mentionPrecheckText` is empty. The new early-drop guard:
```ts
if (isRoom && shouldRequireMention && !wasMentioned && !shouldBypassMention && !mentionPrecheckText) {
return; // fires for empty body
}
```
still fires before `getSenderName()`, `getRoomInfo()`, and `resolveMatrixInboundRoute()` are reached (lines 647, 648, 664). The three removed assertions should still pass and serve as useful guardrails confirming the early-exit optimization is preserved for media-only messages.
Compare this with the poll-response test at line 481 which still retains all three equivalent assertions — it would be consistent to keep them here too.
How can I resolve this? If you propose a fix, please make it concise.…ion patterns The Matrix monitor called buildMentionRegexes(cfg) without agentId, causing agent-level groupChat.mentionPatterns to be silently ignored. Messages matching agent-specific patterns were dropped as no-mention in rooms with requireMention: true. Defer the mention-required drop until after route resolution so the agentId is available for buildMentionRegexes(cfg, route.agentId). Media-only and poll events still drop early (no text to match). Fixes openclaw#51082
…tion and restore media-only test assertions Address Greptile P2 feedback: canDetectMention was not updated when agent-level mentionPatterns matched, causing shouldAckReaction to underfire. Also restore test assertions verifying that media-only unmentioned group messages skip route resolution. Auto-format docs/automation/standing-orders.md per CI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4a87ea2 to
a46e328
Compare
|
Thanks for chasing the same Matrix root cause here. I ran a de-dupe pass on the active cluster around #51082, and this one overlaps directly with #51272 on the same I'm keeping #51272 as the canonical path because it resolves route selection before the mention check, stays narrower in the handler flow, and is currently green across CI. This branch also avoids carrying the deferred-drop shape that triggered extra review and red checks here. Credit to you for surfacing the same fix direction and for the additional review context around mention detection. If there's still behavior left uncovered after #51272 lands, reopen or spin a focused follow-up and link it back here. |
|
@mvanhorn , @vincentkoc - appreciate it! |
Summary
The Matrix plugin's monitor calls
buildMentionRegexes(cfg)without passing theagentId, causing agent-levelgroupChat.mentionPatternsto be silently ignored. Messages matching agent-specific patterns are dropped as "no-mention" in rooms withrequireMention: true.Why this matters
requireMention: trueskip all messages even when they contain valid mention patterns defined at the agent levelextensions/zalouser/src/monitor.ts:472, Discord atextensions/discord/src/monitor/message-handler.preflight.ts:510) already passagentIdcorrectlybuildMentionRegexes(cfg, agentId?)atsrc/auto-reply/reply/mentions.ts:131resolves agent-specific patterns viaresolveMentionPatterns(cfg, agentId)Changes
route.agentIdis availablebuildMentionRegexes(cfg, route.agentId)after route resolutionbuildMentionRegexesmockTesting
This contribution was developed with AI assistance (Claude Code).
Fixes #51082