@openclaw/msteams 2026.2.22 → 2026.2.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/package.json +1 -4
- package/src/attachments/payload.ts +3 -11
- package/src/attachments/shared.test.ts +4 -2
- package/src/attachments.test.ts +674 -486
- package/src/messenger.test.ts +30 -48
- package/src/monitor-handler/message-handler.ts +7 -0
- package/src/policy.test.ts +13 -1
- package/src/policy.ts +2 -0
- package/src/reply-dispatcher.ts +1 -1
package/src/messenger.test.ts
CHANGED
|
@@ -49,6 +49,28 @@ const runtimeStub = {
|
|
|
49
49
|
},
|
|
50
50
|
} as unknown as PluginRuntime;
|
|
51
51
|
|
|
52
|
+
const createNoopAdapter = (): MSTeamsAdapter => ({
|
|
53
|
+
continueConversation: async () => {},
|
|
54
|
+
process: async () => {},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const createRecordedSendActivity = (
|
|
58
|
+
sink: string[],
|
|
59
|
+
failFirstWithStatusCode?: number,
|
|
60
|
+
): ((activity: unknown) => Promise<{ id: string }>) => {
|
|
61
|
+
let attempts = 0;
|
|
62
|
+
return async (activity: unknown) => {
|
|
63
|
+
const { text } = activity as { text?: string };
|
|
64
|
+
const content = text ?? "";
|
|
65
|
+
sink.push(content);
|
|
66
|
+
attempts += 1;
|
|
67
|
+
if (failFirstWithStatusCode !== undefined && attempts === 1) {
|
|
68
|
+
throw Object.assign(new Error("send failed"), { statusCode: failFirstWithStatusCode });
|
|
69
|
+
}
|
|
70
|
+
return { id: `id:${content}` };
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
|
|
52
74
|
describe("msteams messenger", () => {
|
|
53
75
|
beforeEach(() => {
|
|
54
76
|
setMSTeamsRuntime(runtimeStub);
|
|
@@ -117,17 +139,9 @@ describe("msteams messenger", () => {
|
|
|
117
139
|
it("sends thread messages via the provided context", async () => {
|
|
118
140
|
const sent: string[] = [];
|
|
119
141
|
const ctx = {
|
|
120
|
-
sendActivity:
|
|
121
|
-
const { text } = activity as { text?: string };
|
|
122
|
-
sent.push(text ?? "");
|
|
123
|
-
return { id: `id:${text ?? ""}` };
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const adapter: MSTeamsAdapter = {
|
|
128
|
-
continueConversation: async () => {},
|
|
129
|
-
process: async () => {},
|
|
142
|
+
sendActivity: createRecordedSendActivity(sent),
|
|
130
143
|
};
|
|
144
|
+
const adapter = createNoopAdapter();
|
|
131
145
|
|
|
132
146
|
const ids = await sendMSTeamsMessages({
|
|
133
147
|
replyStyle: "thread",
|
|
@@ -149,11 +163,7 @@ describe("msteams messenger", () => {
|
|
|
149
163
|
continueConversation: async (_appId, reference, logic) => {
|
|
150
164
|
seen.reference = reference;
|
|
151
165
|
await logic({
|
|
152
|
-
sendActivity:
|
|
153
|
-
const { text } = activity as { text?: string };
|
|
154
|
-
seen.texts.push(text ?? "");
|
|
155
|
-
return { id: `id:${text ?? ""}` };
|
|
156
|
-
},
|
|
166
|
+
sendActivity: createRecordedSendActivity(seen.texts),
|
|
157
167
|
});
|
|
158
168
|
},
|
|
159
169
|
process: async () => {},
|
|
@@ -192,10 +202,7 @@ describe("msteams messenger", () => {
|
|
|
192
202
|
},
|
|
193
203
|
};
|
|
194
204
|
|
|
195
|
-
const adapter
|
|
196
|
-
continueConversation: async () => {},
|
|
197
|
-
process: async () => {},
|
|
198
|
-
};
|
|
205
|
+
const adapter = createNoopAdapter();
|
|
199
206
|
|
|
200
207
|
const ids = await sendMSTeamsMessages({
|
|
201
208
|
replyStyle: "thread",
|
|
@@ -242,20 +249,9 @@ describe("msteams messenger", () => {
|
|
|
242
249
|
const retryEvents: Array<{ nextAttempt: number; delayMs: number }> = [];
|
|
243
250
|
|
|
244
251
|
const ctx = {
|
|
245
|
-
sendActivity:
|
|
246
|
-
const { text } = activity as { text?: string };
|
|
247
|
-
attempts.push(text ?? "");
|
|
248
|
-
if (attempts.length === 1) {
|
|
249
|
-
throw Object.assign(new Error("throttled"), { statusCode: 429 });
|
|
250
|
-
}
|
|
251
|
-
return { id: `id:${text ?? ""}` };
|
|
252
|
-
},
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
const adapter: MSTeamsAdapter = {
|
|
256
|
-
continueConversation: async () => {},
|
|
257
|
-
process: async () => {},
|
|
252
|
+
sendActivity: createRecordedSendActivity(attempts, 429),
|
|
258
253
|
};
|
|
254
|
+
const adapter = createNoopAdapter();
|
|
259
255
|
|
|
260
256
|
const ids = await sendMSTeamsMessages({
|
|
261
257
|
replyStyle: "thread",
|
|
@@ -280,10 +276,7 @@ describe("msteams messenger", () => {
|
|
|
280
276
|
},
|
|
281
277
|
};
|
|
282
278
|
|
|
283
|
-
const adapter
|
|
284
|
-
continueConversation: async () => {},
|
|
285
|
-
process: async () => {},
|
|
286
|
-
};
|
|
279
|
+
const adapter = createNoopAdapter();
|
|
287
280
|
|
|
288
281
|
await expect(
|
|
289
282
|
sendMSTeamsMessages({
|
|
@@ -303,18 +296,7 @@ describe("msteams messenger", () => {
|
|
|
303
296
|
|
|
304
297
|
const adapter: MSTeamsAdapter = {
|
|
305
298
|
continueConversation: async (_appId, _reference, logic) => {
|
|
306
|
-
await logic({
|
|
307
|
-
sendActivity: async (activity: unknown) => {
|
|
308
|
-
const { text } = activity as { text?: string };
|
|
309
|
-
attempts.push(text ?? "");
|
|
310
|
-
if (attempts.length === 1) {
|
|
311
|
-
throw Object.assign(new Error("server error"), {
|
|
312
|
-
statusCode: 503,
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
return { id: `id:${text ?? ""}` };
|
|
316
|
-
},
|
|
317
|
-
});
|
|
299
|
+
await logic({ sendActivity: createRecordedSendActivity(attempts, 503) });
|
|
318
300
|
},
|
|
319
301
|
process: async () => {},
|
|
320
302
|
};
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
recordPendingHistoryEntryIfEnabled,
|
|
7
7
|
resolveControlCommandGate,
|
|
8
8
|
resolveDefaultGroupPolicy,
|
|
9
|
+
isDangerousNameMatchingEnabled,
|
|
9
10
|
resolveMentionGating,
|
|
10
11
|
formatAllowlistMatchMeta,
|
|
11
12
|
type HistoryEntry,
|
|
@@ -145,10 +146,12 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
|
|
|
145
146
|
|
|
146
147
|
if (dmPolicy !== "open") {
|
|
147
148
|
const effectiveAllowFrom = [...allowFrom.map((v) => String(v)), ...storedAllowFrom];
|
|
149
|
+
const allowNameMatching = isDangerousNameMatchingEnabled(msteamsCfg);
|
|
148
150
|
const allowMatch = resolveMSTeamsAllowlistMatch({
|
|
149
151
|
allowFrom: effectiveAllowFrom,
|
|
150
152
|
senderId,
|
|
151
153
|
senderName,
|
|
154
|
+
allowNameMatching,
|
|
152
155
|
});
|
|
153
156
|
|
|
154
157
|
if (!allowMatch.allowed) {
|
|
@@ -226,10 +229,12 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
|
|
|
226
229
|
return;
|
|
227
230
|
}
|
|
228
231
|
if (effectiveGroupAllowFrom.length > 0) {
|
|
232
|
+
const allowNameMatching = isDangerousNameMatchingEnabled(msteamsCfg);
|
|
229
233
|
const allowMatch = resolveMSTeamsAllowlistMatch({
|
|
230
234
|
allowFrom: effectiveGroupAllowFrom,
|
|
231
235
|
senderId,
|
|
232
236
|
senderName,
|
|
237
|
+
allowNameMatching,
|
|
233
238
|
});
|
|
234
239
|
if (!allowMatch.allowed) {
|
|
235
240
|
log.debug?.("dropping group message (not in groupAllowFrom)", {
|
|
@@ -248,12 +253,14 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
|
|
|
248
253
|
allowFrom: effectiveDmAllowFrom,
|
|
249
254
|
senderId,
|
|
250
255
|
senderName,
|
|
256
|
+
allowNameMatching: isDangerousNameMatchingEnabled(msteamsCfg),
|
|
251
257
|
});
|
|
252
258
|
const groupAllowedForCommands = isMSTeamsGroupAllowed({
|
|
253
259
|
groupPolicy: "allowlist",
|
|
254
260
|
allowFrom: effectiveGroupAllowFrom,
|
|
255
261
|
senderId,
|
|
256
262
|
senderName,
|
|
263
|
+
allowNameMatching: isDangerousNameMatchingEnabled(msteamsCfg),
|
|
257
264
|
});
|
|
258
265
|
const hasControlCommandInMessage = core.channel.text.hasControlCommand(text, cfg);
|
|
259
266
|
const commandGate = resolveControlCommandGate({
|
package/src/policy.test.ts
CHANGED
|
@@ -184,7 +184,7 @@ describe("msteams policy", () => {
|
|
|
184
184
|
).toBe(true);
|
|
185
185
|
});
|
|
186
186
|
|
|
187
|
-
it("
|
|
187
|
+
it("blocks sender-name allowlist matches by default", () => {
|
|
188
188
|
expect(
|
|
189
189
|
isMSTeamsGroupAllowed({
|
|
190
190
|
groupPolicy: "allowlist",
|
|
@@ -192,6 +192,18 @@ describe("msteams policy", () => {
|
|
|
192
192
|
senderId: "other",
|
|
193
193
|
senderName: "User",
|
|
194
194
|
}),
|
|
195
|
+
).toBe(false);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("allows sender-name allowlist matches when explicitly enabled", () => {
|
|
199
|
+
expect(
|
|
200
|
+
isMSTeamsGroupAllowed({
|
|
201
|
+
groupPolicy: "allowlist",
|
|
202
|
+
allowFrom: ["user"],
|
|
203
|
+
senderId: "other",
|
|
204
|
+
senderName: "User",
|
|
205
|
+
allowNameMatching: true,
|
|
206
|
+
}),
|
|
195
207
|
).toBe(true);
|
|
196
208
|
});
|
|
197
209
|
|
package/src/policy.ts
CHANGED
|
@@ -209,6 +209,7 @@ export function resolveMSTeamsAllowlistMatch(params: {
|
|
|
209
209
|
allowFrom: Array<string | number>;
|
|
210
210
|
senderId: string;
|
|
211
211
|
senderName?: string | null;
|
|
212
|
+
allowNameMatching?: boolean;
|
|
212
213
|
}): MSTeamsAllowlistMatch {
|
|
213
214
|
return resolveAllowlistMatchSimple(params);
|
|
214
215
|
}
|
|
@@ -245,6 +246,7 @@ export function isMSTeamsGroupAllowed(params: {
|
|
|
245
246
|
allowFrom: Array<string | number>;
|
|
246
247
|
senderId: string;
|
|
247
248
|
senderName?: string | null;
|
|
249
|
+
allowNameMatching?: boolean;
|
|
248
250
|
}): boolean {
|
|
249
251
|
const { groupPolicy } = params;
|
|
250
252
|
if (groupPolicy === "disabled") {
|
package/src/reply-dispatcher.ts
CHANGED
|
@@ -68,6 +68,7 @@ export function createMSTeamsReplyDispatcher(params: {
|
|
|
68
68
|
core.channel.reply.createReplyDispatcherWithTyping({
|
|
69
69
|
...prefixOptions,
|
|
70
70
|
humanDelay: core.channel.reply.resolveHumanDelayConfig(params.cfg, params.agentId),
|
|
71
|
+
typingCallbacks,
|
|
71
72
|
deliver: async (payload) => {
|
|
72
73
|
const tableMode = core.channel.text.resolveMarkdownTableMode({
|
|
73
74
|
cfg: params.cfg,
|
|
@@ -121,7 +122,6 @@ export function createMSTeamsReplyDispatcher(params: {
|
|
|
121
122
|
hint,
|
|
122
123
|
});
|
|
123
124
|
},
|
|
124
|
-
onReplyStart: typingCallbacks.onReplyStart,
|
|
125
125
|
});
|
|
126
126
|
|
|
127
127
|
return {
|