fix(slack): suppress NO_REPLY before Slack API call

Guard sendMessageSlack against NO_REPLY tokens reaching the Slack API,
which caused truncated push notifications before the reply filter could
intercept them.

Made-with: Cursor
(cherry picked from commit fab9b52039)
This commit is contained in:
SidQin-cyber
2026-02-26 20:53:24 +08:00
committed by Peter Steinberger
parent 9c142993b8
commit eb9a968336
2 changed files with 51 additions and 0 deletions

View File

@@ -4,6 +4,52 @@ import { createSlackSendTestClient, installSlackBlockTestMocks } from "./blocks.
installSlackBlockTestMocks();
const { sendMessageSlack } = await import("./send.js");
describe("sendMessageSlack NO_REPLY guard", () => {
it("suppresses NO_REPLY text before any Slack API call", async () => {
const client = createSlackSendTestClient();
const result = await sendMessageSlack("channel:C123", "NO_REPLY", {
token: "xoxb-test",
client,
});
expect(client.chat.postMessage).not.toHaveBeenCalled();
expect(result.messageId).toBe("suppressed");
});
it("suppresses NO_REPLY with surrounding whitespace", async () => {
const client = createSlackSendTestClient();
const result = await sendMessageSlack("channel:C123", " NO_REPLY ", {
token: "xoxb-test",
client,
});
expect(client.chat.postMessage).not.toHaveBeenCalled();
expect(result.messageId).toBe("suppressed");
});
it("does not suppress substantive text containing NO_REPLY", async () => {
const client = createSlackSendTestClient();
await sendMessageSlack("channel:C123", "This is not a NO_REPLY situation", {
token: "xoxb-test",
client,
});
expect(client.chat.postMessage).toHaveBeenCalled();
});
it("does not suppress NO_REPLY when blocks are attached", async () => {
const client = createSlackSendTestClient();
const result = await sendMessageSlack("channel:C123", "NO_REPLY", {
token: "xoxb-test",
client,
blocks: [{ type: "section", text: { type: "mrkdwn", text: "content" } }],
});
expect(client.chat.postMessage).toHaveBeenCalled();
expect(result.messageId).toBe("171234.567");
});
});
describe("sendMessageSlack blocks", () => {
it("posts blocks with fallback text when message is empty", async () => {
const client = createSlackSendTestClient();

View File

@@ -9,6 +9,7 @@ import {
resolveChunkMode,
resolveTextChunkLimit,
} from "../auto-reply/chunk.js";
import { isSilentReplyText } from "../auto-reply/tokens.js";
import { loadConfig } from "../config/config.js";
import { resolveMarkdownTableMode } from "../config/markdown-tables.js";
import { logVerbose } from "../globals.js";
@@ -231,6 +232,10 @@ export async function sendMessageSlack(
opts: SlackSendOpts = {},
): Promise<SlackSendResult> {
const trimmedMessage = message?.trim() ?? "";
if (isSilentReplyText(trimmedMessage) && !opts.mediaUrl && !opts.blocks) {
logVerbose("slack send: suppressed NO_REPLY token before API call");
return { messageId: "suppressed", channelId: "" };
}
const blocks = opts.blocks == null ? undefined : validateSlackBlocksArray(opts.blocks);
if (!trimmedMessage && !opts.mediaUrl && !blocks) {
throw new Error("Slack send requires text, blocks, or media");