fix(slack): stop block-based sender rehydration on assistant message edits (#71700)

* fix(slack): stop block-based sender rehydration on message edits

* docs(changelog): note Slack sender attribution fix
This commit is contained in:
Vincent Koc
2026-04-25 12:34:55 -07:00
committed by GitHub
parent bb6cf75463
commit 1915b29a3c
3 changed files with 30 additions and 20 deletions

View File

@@ -187,6 +187,7 @@ Docs: https://docs.openclaw.ai
- Sessions: make `sessions_spawn(mode="session")` errors name usable alternatives when the current channel cannot bind subagent threads. Fixes #67400. (#67790) Thanks @stainlu.
- Agents/Claude CLI: pass the OpenClaw system prompt through Claude's prompt-file flag so Windows runs avoid argv length failures without changing system prompt semantics. Fixes #69158. (#69211) Thanks @skylee-01, @cassioanorte, @Syu0, and @Stache73.
- Agents/CLI sessions: bind `google-gemini-cli` session auth-epoch to the Google account identity in `~/.gemini/oauth_creds.json`, so Gemini-backed agents resume their conversation after gateway restart instead of minting a fresh session, and stale bindings are invalidated when the authenticated Google account changes. Fixes #70973. (#71076) Thanks @openperf.
- Slack: stop treating user mentions in assistant-authored message edit blocks as sender attribution, preventing edited bot messages from spoofing a mentioned DM user. (#71700) Thanks @vincentkoc.
## 2026.4.24

View File

@@ -271,6 +271,35 @@ describe("registerSlackMessageEvents", () => {
expect(messageQueueMock).not.toHaveBeenCalled();
});
it("drops self-authored message_changed events that only include block user IDs", async () => {
const { handleSlackMessage } = await invokeRegisteredHandler({
eventName: "message",
overrides: { dmPolicy: "open" },
event: {
...makeAssistantChangedEvent(),
message: {
ts: "123.456",
user: "U_BOT",
text: "preview edit with mention",
blocks: [
{
type: "rich_text",
elements: [
{
type: "rich_text_section",
elements: [{ type: "user", user_id: "UREAL123" }],
},
],
},
],
},
},
});
expect(handleSlackMessage).not.toHaveBeenCalled();
expect(messageQueueMock).not.toHaveBeenCalled();
});
it("handles channel and group messages via the unified message handler", async () => {
const { handler, handleSlackMessage } = createHandlers("message", {
dmPolicy: "open",

View File

@@ -59,31 +59,12 @@ function collectMetadataUserCandidates(
}
}
function collectBlockUserIds(candidates: Set<string>, value: unknown, botUserId: string): void {
if (Array.isArray(value)) {
for (const entry of value) {
collectBlockUserIds(candidates, entry, botUserId);
}
return;
}
const record = asRecord(value);
if (!record) {
return;
}
addUserCandidate(candidates, record.user_id, botUserId);
for (const key of ["elements", "accessory", "fields"]) {
collectBlockUserIds(candidates, record[key], botUserId);
}
}
function resolveAssistantMessageChangedSender(params: {
event: SlackMessageChangedEvent;
message?: SlackAssistantMessageRecord;
botUserId: string;
}): string | undefined {
const candidates = new Set<string>();
collectMetadataUserCandidates(candidates, params.message?.metadata, params.botUserId);
collectBlockUserIds(candidates, params.message?.blocks, params.botUserId);
return candidates.size === 1 ? [...candidates][0] : undefined;
}
@@ -122,7 +103,6 @@ function resolveAssistantMessageChangedInbound(params: {
return undefined;
}
const senderId = resolveAssistantMessageChangedSender({
event: changed,
message,
botUserId: params.ctx.botUserId,
});