@openclaw/msteams 2026.5.2 → 2026.5.3-beta.1
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/dist/api.js +3 -0
- package/dist/channel-D7hdreTh.js +984 -0
- package/dist/channel-config-api.js +2 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-BC1ruIfN.js +573 -0
- package/dist/config-schema-B8QezH6t.js +15 -0
- package/dist/contract-api.js +2 -0
- package/dist/graph-users-9uQJepqr.js +1354 -0
- package/dist/index.js +22 -0
- package/dist/oauth-BWJyilR1.js +114 -0
- package/dist/oauth.token-xxpoLWy5.js +115 -0
- package/dist/policy-DTnU2GR7.js +142 -0
- package/dist/probe-D_H8yFps.js +2194 -0
- package/dist/resolve-allowlist-D41JSziq.js +219 -0
- package/dist/runtime-api-DV1iVMn1.js +28 -0
- package/dist/runtime-api.js +2 -0
- package/dist/secret-contract-BuoEXmPS.js +35 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/setup-entry.js +15 -0
- package/dist/setup-plugin-api.js +64 -0
- package/dist/setup-surface-BLkFQYIQ.js +313 -0
- package/dist/src-CFp1QpFd.js +4064 -0
- package/dist/test-api.js +2 -0
- package/package.json +14 -6
- package/api.ts +0 -3
- package/channel-config-api.ts +0 -1
- package/channel-plugin-api.ts +0 -2
- package/config-api.ts +0 -4
- package/contract-api.ts +0 -4
- package/index.ts +0 -20
- package/runtime-api.ts +0 -73
- package/secret-contract-api.ts +0 -5
- package/setup-entry.ts +0 -13
- package/setup-plugin-api.ts +0 -3
- package/src/ai-entity.ts +0 -7
- package/src/approval-auth.ts +0 -44
- package/src/attachments/bot-framework.test.ts +0 -461
- package/src/attachments/bot-framework.ts +0 -362
- package/src/attachments/download.ts +0 -311
- package/src/attachments/graph.test.ts +0 -416
- package/src/attachments/graph.ts +0 -484
- package/src/attachments/html.ts +0 -122
- package/src/attachments/payload.ts +0 -14
- package/src/attachments/remote-media.test.ts +0 -137
- package/src/attachments/remote-media.ts +0 -112
- package/src/attachments/shared.test.ts +0 -530
- package/src/attachments/shared.ts +0 -626
- package/src/attachments/types.ts +0 -47
- package/src/attachments.graph.test.ts +0 -342
- package/src/attachments.helpers.test.ts +0 -246
- package/src/attachments.test-helpers.ts +0 -17
- package/src/attachments.test.ts +0 -687
- package/src/attachments.ts +0 -18
- package/src/block-streaming-config.test.ts +0 -61
- package/src/channel-api.ts +0 -1
- package/src/channel.actions.test.ts +0 -742
- package/src/channel.directory.test.ts +0 -200
- package/src/channel.runtime.ts +0 -56
- package/src/channel.setup.ts +0 -77
- package/src/channel.test.ts +0 -128
- package/src/channel.ts +0 -1136
- package/src/config-schema.ts +0 -6
- package/src/config-ui-hints.ts +0 -12
- package/src/conversation-store-fs.test.ts +0 -74
- package/src/conversation-store-fs.ts +0 -149
- package/src/conversation-store-helpers.test.ts +0 -202
- package/src/conversation-store-helpers.ts +0 -105
- package/src/conversation-store-memory.ts +0 -51
- package/src/conversation-store.shared.test.ts +0 -225
- package/src/conversation-store.ts +0 -71
- package/src/directory-live.test.ts +0 -156
- package/src/directory-live.ts +0 -111
- package/src/doctor.ts +0 -27
- package/src/errors.test.ts +0 -133
- package/src/errors.ts +0 -246
- package/src/feedback-reflection-prompt.ts +0 -117
- package/src/feedback-reflection-store.ts +0 -114
- package/src/feedback-reflection.test.ts +0 -237
- package/src/feedback-reflection.ts +0 -283
- package/src/file-consent-helpers.test.ts +0 -326
- package/src/file-consent-helpers.ts +0 -126
- package/src/file-consent-invoke.ts +0 -150
- package/src/file-consent.test.ts +0 -363
- package/src/file-consent.ts +0 -287
- package/src/graph-chat.ts +0 -55
- package/src/graph-group-management.test.ts +0 -318
- package/src/graph-group-management.ts +0 -168
- package/src/graph-members.test.ts +0 -89
- package/src/graph-members.ts +0 -48
- package/src/graph-messages.actions.test.ts +0 -243
- package/src/graph-messages.read.test.ts +0 -391
- package/src/graph-messages.search.test.ts +0 -213
- package/src/graph-messages.test-helpers.ts +0 -50
- package/src/graph-messages.ts +0 -534
- package/src/graph-teams.test.ts +0 -215
- package/src/graph-teams.ts +0 -114
- package/src/graph-thread.test.ts +0 -246
- package/src/graph-thread.ts +0 -146
- package/src/graph-upload.test.ts +0 -258
- package/src/graph-upload.ts +0 -531
- package/src/graph-users.ts +0 -29
- package/src/graph.test.ts +0 -516
- package/src/graph.ts +0 -293
- package/src/inbound.test.ts +0 -221
- package/src/inbound.ts +0 -148
- package/src/index.ts +0 -4
- package/src/media-helpers.test.ts +0 -202
- package/src/media-helpers.ts +0 -105
- package/src/mentions.test.ts +0 -244
- package/src/mentions.ts +0 -114
- package/src/messenger.test.ts +0 -865
- package/src/messenger.ts +0 -605
- package/src/monitor-handler/access.ts +0 -125
- package/src/monitor-handler/inbound-media.test.ts +0 -289
- package/src/monitor-handler/inbound-media.ts +0 -180
- package/src/monitor-handler/message-handler-mock-support.test-support.ts +0 -28
- package/src/monitor-handler/message-handler.authz.test.ts +0 -669
- package/src/monitor-handler/message-handler.dm-media.test.ts +0 -54
- package/src/monitor-handler/message-handler.test-support.ts +0 -100
- package/src/monitor-handler/message-handler.thread-parent.test.ts +0 -223
- package/src/monitor-handler/message-handler.thread-session.test.ts +0 -77
- package/src/monitor-handler/message-handler.ts +0 -1000
- package/src/monitor-handler/reaction-handler.test.ts +0 -267
- package/src/monitor-handler/reaction-handler.ts +0 -210
- package/src/monitor-handler/thread-session.ts +0 -17
- package/src/monitor-handler.adaptive-card.test.ts +0 -162
- package/src/monitor-handler.feedback-authz.test.ts +0 -314
- package/src/monitor-handler.file-consent.test.ts +0 -423
- package/src/monitor-handler.sso.test.ts +0 -563
- package/src/monitor-handler.test-helpers.ts +0 -180
- package/src/monitor-handler.ts +0 -534
- package/src/monitor-handler.types.ts +0 -27
- package/src/monitor-types.ts +0 -6
- package/src/monitor.lifecycle.test.ts +0 -278
- package/src/monitor.test.ts +0 -119
- package/src/monitor.ts +0 -442
- package/src/oauth.flow.ts +0 -77
- package/src/oauth.shared.ts +0 -37
- package/src/oauth.test.ts +0 -305
- package/src/oauth.token.ts +0 -158
- package/src/oauth.ts +0 -130
- package/src/outbound.test.ts +0 -130
- package/src/outbound.ts +0 -71
- package/src/pending-uploads-fs.test.ts +0 -246
- package/src/pending-uploads-fs.ts +0 -235
- package/src/pending-uploads.test.ts +0 -173
- package/src/pending-uploads.ts +0 -121
- package/src/policy.test.ts +0 -240
- package/src/policy.ts +0 -262
- package/src/polls-store-memory.ts +0 -32
- package/src/polls.test.ts +0 -160
- package/src/polls.ts +0 -323
- package/src/presentation.ts +0 -68
- package/src/probe.test.ts +0 -77
- package/src/probe.ts +0 -132
- package/src/reply-dispatcher.test.ts +0 -437
- package/src/reply-dispatcher.ts +0 -346
- package/src/reply-stream-controller.test.ts +0 -235
- package/src/reply-stream-controller.ts +0 -147
- package/src/resolve-allowlist.test.ts +0 -250
- package/src/resolve-allowlist.ts +0 -309
- package/src/revoked-context.ts +0 -17
- package/src/runtime.ts +0 -9
- package/src/sdk-types.ts +0 -59
- package/src/sdk.test.ts +0 -666
- package/src/sdk.ts +0 -884
- package/src/secret-contract.ts +0 -49
- package/src/secret-input.ts +0 -7
- package/src/send-context.ts +0 -231
- package/src/send.test.ts +0 -493
- package/src/send.ts +0 -637
- package/src/sent-message-cache.test.ts +0 -15
- package/src/sent-message-cache.ts +0 -56
- package/src/session-route.ts +0 -40
- package/src/setup-core.ts +0 -160
- package/src/setup-surface.test.ts +0 -202
- package/src/setup-surface.ts +0 -320
- package/src/sso-token-store.test.ts +0 -72
- package/src/sso-token-store.ts +0 -166
- package/src/sso.ts +0 -300
- package/src/storage.ts +0 -25
- package/src/store-fs.ts +0 -44
- package/src/streaming-message.test.ts +0 -262
- package/src/streaming-message.ts +0 -297
- package/src/test-runtime.ts +0 -16
- package/src/thread-parent-context.test.ts +0 -224
- package/src/thread-parent-context.ts +0 -159
- package/src/token-response.ts +0 -11
- package/src/token.test.ts +0 -259
- package/src/token.ts +0 -195
- package/src/user-agent.test.ts +0 -86
- package/src/user-agent.ts +0 -53
- package/src/webhook-timeouts.ts +0 -27
- package/src/welcome-card.test.ts +0 -81
- package/src/welcome-card.ts +0 -57
- package/test-api.ts +0 -1
- package/tsconfig.json +0 -16
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from "node:events";
|
|
2
|
-
import type { Request, Response } from "express";
|
|
3
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
4
|
-
import type { OpenClawConfig, RuntimeEnv } from "../runtime-api.js";
|
|
5
|
-
import type { MSTeamsConversationStore } from "./conversation-store.js";
|
|
6
|
-
import type { MSTeamsPollStore } from "./polls.js";
|
|
7
|
-
|
|
8
|
-
type FakeServer = EventEmitter & {
|
|
9
|
-
close: (callback?: (err?: Error | null) => void) => void;
|
|
10
|
-
setTimeout: (msecs: number) => FakeServer;
|
|
11
|
-
requestTimeout: number;
|
|
12
|
-
headersTimeout: number;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const expressControl = vi.hoisted(() => ({
|
|
16
|
-
mode: { value: "listening" as "listening" | "error" },
|
|
17
|
-
apps: [] as Array<{
|
|
18
|
-
use: ReturnType<typeof vi.fn>;
|
|
19
|
-
post: ReturnType<typeof vi.fn>;
|
|
20
|
-
listen: ReturnType<typeof vi.fn>;
|
|
21
|
-
}>,
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
|
-
vi.mock("../runtime-api.js", () => ({
|
|
25
|
-
DEFAULT_WEBHOOK_MAX_BODY_BYTES: 1024 * 1024,
|
|
26
|
-
normalizeSecretInputString: (value: unknown) =>
|
|
27
|
-
typeof value === "string" && value.trim() ? value.trim() : undefined,
|
|
28
|
-
hasConfiguredSecretInput: (value: unknown) =>
|
|
29
|
-
typeof value === "string" && value.trim().length > 0,
|
|
30
|
-
normalizeResolvedSecretInputString: (params: { value?: unknown }) =>
|
|
31
|
-
typeof params?.value === "string" && params.value.trim() ? params.value.trim() : undefined,
|
|
32
|
-
keepHttpServerTaskAlive: vi.fn(
|
|
33
|
-
async (params: { abortSignal?: AbortSignal; onAbort?: () => Promise<void> | void }) => {
|
|
34
|
-
await new Promise<void>((resolve) => {
|
|
35
|
-
if (params.abortSignal?.aborted) {
|
|
36
|
-
resolve();
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
params.abortSignal?.addEventListener("abort", () => resolve(), { once: true });
|
|
40
|
-
});
|
|
41
|
-
await params.onAbort?.();
|
|
42
|
-
},
|
|
43
|
-
),
|
|
44
|
-
mergeAllowlist: (params: { existing?: string[]; additions?: string[] }) =>
|
|
45
|
-
Array.from(new Set([...(params.existing ?? []), ...(params.additions ?? [])])),
|
|
46
|
-
summarizeMapping: vi.fn(),
|
|
47
|
-
}));
|
|
48
|
-
|
|
49
|
-
vi.mock("express", () => {
|
|
50
|
-
const json = vi.fn(() => {
|
|
51
|
-
return (_req: unknown, _res: unknown, next?: (err?: unknown) => void) => {
|
|
52
|
-
next?.();
|
|
53
|
-
};
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const factory = () => ({
|
|
57
|
-
use: vi.fn(),
|
|
58
|
-
post: vi.fn(),
|
|
59
|
-
listen: vi.fn((_port: number) => {
|
|
60
|
-
const server = new EventEmitter() as FakeServer;
|
|
61
|
-
server.setTimeout = vi.fn((_msecs: number) => server);
|
|
62
|
-
server.requestTimeout = 0;
|
|
63
|
-
server.headersTimeout = 0;
|
|
64
|
-
server.close = (callback?: (err?: Error | null) => void) => {
|
|
65
|
-
queueMicrotask(() => {
|
|
66
|
-
server.emit("close");
|
|
67
|
-
callback?.(null);
|
|
68
|
-
});
|
|
69
|
-
};
|
|
70
|
-
queueMicrotask(() => {
|
|
71
|
-
if (expressControl.mode.value === "error") {
|
|
72
|
-
server.emit("error", new Error("listen EADDRINUSE"));
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
server.emit("listening");
|
|
76
|
-
});
|
|
77
|
-
return server;
|
|
78
|
-
}),
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
const wrappedFactory = () => {
|
|
82
|
-
const app = factory();
|
|
83
|
-
expressControl.apps.push(app);
|
|
84
|
-
return app;
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
default: wrappedFactory,
|
|
89
|
-
json,
|
|
90
|
-
};
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const registerMSTeamsHandlers = vi.hoisted(() =>
|
|
94
|
-
vi.fn(() => ({
|
|
95
|
-
run: vi.fn(async () => {}),
|
|
96
|
-
})),
|
|
97
|
-
);
|
|
98
|
-
const createMSTeamsAdapter = vi.hoisted(() =>
|
|
99
|
-
vi.fn(() => ({
|
|
100
|
-
process: vi.fn(async () => {}),
|
|
101
|
-
})),
|
|
102
|
-
);
|
|
103
|
-
const jwtValidate = vi.hoisted(() => vi.fn().mockResolvedValue(true));
|
|
104
|
-
const loadMSTeamsSdkWithAuth = vi.hoisted(() =>
|
|
105
|
-
vi.fn(async () => ({
|
|
106
|
-
sdk: {
|
|
107
|
-
ActivityHandler: function ActivityHandler() {},
|
|
108
|
-
MsalTokenProvider: function MsalTokenProvider() {},
|
|
109
|
-
authorizeJWT:
|
|
110
|
-
() => (_req: unknown, _res: unknown, next: ((err?: unknown) => void) | undefined) =>
|
|
111
|
-
next?.(),
|
|
112
|
-
},
|
|
113
|
-
authConfig: {},
|
|
114
|
-
})),
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
vi.mock("./monitor-handler.js", () => ({
|
|
118
|
-
registerMSTeamsHandlers: () => registerMSTeamsHandlers(),
|
|
119
|
-
}));
|
|
120
|
-
|
|
121
|
-
vi.mock("./resolve-allowlist.js", () => ({
|
|
122
|
-
resolveMSTeamsChannelAllowlist: vi.fn(async () => []),
|
|
123
|
-
resolveMSTeamsUserAllowlist: vi.fn(async () => []),
|
|
124
|
-
}));
|
|
125
|
-
|
|
126
|
-
vi.mock("./sdk.js", () => ({
|
|
127
|
-
createMSTeamsAdapter: () => createMSTeamsAdapter(),
|
|
128
|
-
loadMSTeamsSdkWithAuth: () => loadMSTeamsSdkWithAuth(),
|
|
129
|
-
createMSTeamsTokenProvider: () => ({
|
|
130
|
-
getAccessToken: vi.fn().mockResolvedValue("mock-token"),
|
|
131
|
-
}),
|
|
132
|
-
createBotFrameworkJwtValidator: vi.fn().mockResolvedValue({
|
|
133
|
-
validate: jwtValidate,
|
|
134
|
-
}),
|
|
135
|
-
}));
|
|
136
|
-
|
|
137
|
-
vi.mock("./runtime.js", () => ({
|
|
138
|
-
getMSTeamsRuntime: () => ({
|
|
139
|
-
logging: {
|
|
140
|
-
getChildLogger: () => ({
|
|
141
|
-
info: vi.fn(),
|
|
142
|
-
error: vi.fn(),
|
|
143
|
-
debug: vi.fn(),
|
|
144
|
-
}),
|
|
145
|
-
},
|
|
146
|
-
channel: {
|
|
147
|
-
text: {
|
|
148
|
-
resolveTextChunkLimit: () => 4000,
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
}),
|
|
152
|
-
}));
|
|
153
|
-
|
|
154
|
-
import { monitorMSTeamsProvider } from "./monitor.js";
|
|
155
|
-
|
|
156
|
-
function createConfig(port: number): OpenClawConfig {
|
|
157
|
-
return {
|
|
158
|
-
channels: {
|
|
159
|
-
msteams: {
|
|
160
|
-
enabled: true,
|
|
161
|
-
appId: "app-id",
|
|
162
|
-
appPassword: "app-password", // pragma: allowlist secret
|
|
163
|
-
tenantId: "tenant-id",
|
|
164
|
-
webhook: {
|
|
165
|
-
port,
|
|
166
|
-
path: "/api/messages",
|
|
167
|
-
},
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
} as OpenClawConfig;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function createRuntime(): RuntimeEnv {
|
|
174
|
-
return {
|
|
175
|
-
log: vi.fn(),
|
|
176
|
-
error: vi.fn(),
|
|
177
|
-
exit: (code: number): never => {
|
|
178
|
-
throw new Error(`exit ${code}`);
|
|
179
|
-
},
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function createStores() {
|
|
184
|
-
return {
|
|
185
|
-
conversationStore: {} as MSTeamsConversationStore,
|
|
186
|
-
pollStore: {} as MSTeamsPollStore,
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
describe("monitorMSTeamsProvider lifecycle", () => {
|
|
191
|
-
afterEach(() => {
|
|
192
|
-
vi.clearAllMocks();
|
|
193
|
-
expressControl.mode.value = "listening";
|
|
194
|
-
expressControl.apps.length = 0;
|
|
195
|
-
jwtValidate.mockReset().mockResolvedValue(true);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it("stays active until aborted", async () => {
|
|
199
|
-
const abort = new AbortController();
|
|
200
|
-
const stores = createStores();
|
|
201
|
-
const task = monitorMSTeamsProvider({
|
|
202
|
-
cfg: createConfig(0),
|
|
203
|
-
runtime: createRuntime(),
|
|
204
|
-
abortSignal: abort.signal,
|
|
205
|
-
conversationStore: stores.conversationStore,
|
|
206
|
-
pollStore: stores.pollStore,
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
const early = await Promise.race([
|
|
210
|
-
task.then(() => "resolved"),
|
|
211
|
-
new Promise<"pending">((resolve) => setTimeout(() => resolve("pending"), 50)),
|
|
212
|
-
]);
|
|
213
|
-
expect(early).toBe("pending");
|
|
214
|
-
|
|
215
|
-
abort.abort();
|
|
216
|
-
const result = await task;
|
|
217
|
-
expect(result.app).not.toBeNull();
|
|
218
|
-
await expect(result.shutdown()).resolves.toBeUndefined();
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it("rejects startup when webhook port is already in use", async () => {
|
|
222
|
-
expressControl.mode.value = "error";
|
|
223
|
-
await expect(
|
|
224
|
-
monitorMSTeamsProvider({
|
|
225
|
-
cfg: createConfig(3978),
|
|
226
|
-
runtime: createRuntime(),
|
|
227
|
-
abortSignal: new AbortController().signal,
|
|
228
|
-
conversationStore: createStores().conversationStore,
|
|
229
|
-
pollStore: createStores().pollStore,
|
|
230
|
-
}),
|
|
231
|
-
).rejects.toThrow(/EADDRINUSE/);
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it("runs JWT validation before JSON body parsing", async () => {
|
|
235
|
-
const abort = new AbortController();
|
|
236
|
-
const task = monitorMSTeamsProvider({
|
|
237
|
-
cfg: createConfig(0),
|
|
238
|
-
runtime: createRuntime(),
|
|
239
|
-
abortSignal: abort.signal,
|
|
240
|
-
conversationStore: createStores().conversationStore,
|
|
241
|
-
pollStore: createStores().pollStore,
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
await new Promise<void>((resolve) => setTimeout(resolve, 0));
|
|
245
|
-
|
|
246
|
-
const app = expressControl.apps.at(-1);
|
|
247
|
-
expect(app).toBeDefined();
|
|
248
|
-
expect(app!.use).toHaveBeenCalledTimes(4);
|
|
249
|
-
|
|
250
|
-
const jsonMiddleware = vi.mocked((await import("express")).json).mock.results[0]?.value;
|
|
251
|
-
expect(jsonMiddleware).toBeDefined();
|
|
252
|
-
expect(app!.use.mock.calls[1]?.[0]).not.toBe(jsonMiddleware);
|
|
253
|
-
expect(app!.use.mock.calls[2]?.[0]).toBe(jsonMiddleware);
|
|
254
|
-
|
|
255
|
-
const jwtMiddleware = app!.use.mock.calls[1]?.[0] as (
|
|
256
|
-
req: Request,
|
|
257
|
-
res: Response,
|
|
258
|
-
next: (err?: unknown) => void,
|
|
259
|
-
) => void;
|
|
260
|
-
const next = vi.fn();
|
|
261
|
-
jwtMiddleware(
|
|
262
|
-
{ headers: { authorization: "Bearer token" } } as Request,
|
|
263
|
-
{
|
|
264
|
-
status: vi.fn().mockReturnThis(),
|
|
265
|
-
json: vi.fn(),
|
|
266
|
-
} as unknown as Response,
|
|
267
|
-
next,
|
|
268
|
-
);
|
|
269
|
-
|
|
270
|
-
await vi.waitFor(() => {
|
|
271
|
-
expect(jwtValidate).toHaveBeenCalledWith("Bearer token");
|
|
272
|
-
expect(next).toHaveBeenCalledTimes(1);
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
abort.abort();
|
|
276
|
-
await task;
|
|
277
|
-
});
|
|
278
|
-
});
|
package/src/monitor.test.ts
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { once } from "node:events";
|
|
2
|
-
import type { Server } from "node:http";
|
|
3
|
-
import { createConnection, type AddressInfo } from "node:net";
|
|
4
|
-
import express from "express";
|
|
5
|
-
import { describe, expect, it } from "vitest";
|
|
6
|
-
import { applyMSTeamsWebhookTimeouts } from "./webhook-timeouts.js";
|
|
7
|
-
|
|
8
|
-
async function closeServer(server: Server): Promise<void> {
|
|
9
|
-
await new Promise<void>((resolve) => {
|
|
10
|
-
server.close(() => resolve());
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async function waitForSlowBodySocketClose(port: number, timeoutMs: number): Promise<number> {
|
|
15
|
-
return new Promise<number>((resolve, reject) => {
|
|
16
|
-
const startedAt = Date.now();
|
|
17
|
-
const socket = createConnection({ host: "127.0.0.1", port }, () => {
|
|
18
|
-
socket.write("POST /api/messages HTTP/1.1\r\n");
|
|
19
|
-
socket.write("Host: localhost\r\n");
|
|
20
|
-
socket.write("Content-Type: application/json\r\n");
|
|
21
|
-
socket.write("Content-Length: 1048576\r\n");
|
|
22
|
-
socket.write("\r\n");
|
|
23
|
-
socket.write('{"type":"message"');
|
|
24
|
-
});
|
|
25
|
-
socket.on("error", () => {
|
|
26
|
-
// ECONNRESET is expected once the server drops the socket.
|
|
27
|
-
});
|
|
28
|
-
const failTimer = setTimeout(() => {
|
|
29
|
-
socket.destroy();
|
|
30
|
-
reject(new Error(`socket stayed open for ${timeoutMs}ms`));
|
|
31
|
-
}, timeoutMs);
|
|
32
|
-
socket.on("close", () => {
|
|
33
|
-
clearTimeout(failTimer);
|
|
34
|
-
resolve(Date.now() - startedAt);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
describe("msteams monitor webhook hardening", () => {
|
|
40
|
-
it("applies default timeouts and header clamp", async () => {
|
|
41
|
-
const app = express();
|
|
42
|
-
const server = app.listen(0, "127.0.0.1");
|
|
43
|
-
await once(server, "listening");
|
|
44
|
-
try {
|
|
45
|
-
applyMSTeamsWebhookTimeouts(server);
|
|
46
|
-
|
|
47
|
-
expect(server.timeout).toBe(30_000);
|
|
48
|
-
expect(server.requestTimeout).toBe(30_000);
|
|
49
|
-
expect(server.headersTimeout).toBe(15_000);
|
|
50
|
-
} finally {
|
|
51
|
-
await closeServer(server);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("applies explicit webhook timeout values", async () => {
|
|
56
|
-
const app = express();
|
|
57
|
-
const server = app.listen(0, "127.0.0.1");
|
|
58
|
-
await once(server, "listening");
|
|
59
|
-
try {
|
|
60
|
-
applyMSTeamsWebhookTimeouts(server, {
|
|
61
|
-
inactivityTimeoutMs: 3210,
|
|
62
|
-
requestTimeoutMs: 6543,
|
|
63
|
-
headersTimeoutMs: 9876,
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
expect(server.timeout).toBe(3210);
|
|
67
|
-
expect(server.requestTimeout).toBe(6543);
|
|
68
|
-
expect(server.headersTimeout).toBe(6543);
|
|
69
|
-
} finally {
|
|
70
|
-
await closeServer(server);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("clamps headers timeout when explicit value exceeds request timeout", async () => {
|
|
75
|
-
const app = express();
|
|
76
|
-
const server = app.listen(0, "127.0.0.1");
|
|
77
|
-
await once(server, "listening");
|
|
78
|
-
try {
|
|
79
|
-
applyMSTeamsWebhookTimeouts(server, {
|
|
80
|
-
inactivityTimeoutMs: 12_000,
|
|
81
|
-
requestTimeoutMs: 9_000,
|
|
82
|
-
headersTimeoutMs: 15_000,
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
expect(server.timeout).toBe(12_000);
|
|
86
|
-
expect(server.requestTimeout).toBe(9_000);
|
|
87
|
-
expect(server.headersTimeout).toBe(9_000);
|
|
88
|
-
} finally {
|
|
89
|
-
await closeServer(server);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it("drops slow-body webhook requests within configured inactivity timeout", async () => {
|
|
94
|
-
const app = express();
|
|
95
|
-
app.use(express.json({ limit: "1mb" }));
|
|
96
|
-
app.use((_req, res, _next) => {
|
|
97
|
-
res.status(401).end("unauthorized");
|
|
98
|
-
});
|
|
99
|
-
app.post("/api/messages", (_req, res) => {
|
|
100
|
-
res.end("ok");
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
const server = app.listen(0, "127.0.0.1");
|
|
104
|
-
await once(server, "listening");
|
|
105
|
-
try {
|
|
106
|
-
applyMSTeamsWebhookTimeouts(server, {
|
|
107
|
-
inactivityTimeoutMs: 400,
|
|
108
|
-
requestTimeoutMs: 1500,
|
|
109
|
-
headersTimeoutMs: 1500,
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
const port = (server.address() as AddressInfo).port;
|
|
113
|
-
const closedMs = await waitForSlowBodySocketClose(port, 3000);
|
|
114
|
-
expect(closedMs).toBeLessThan(2500);
|
|
115
|
-
} finally {
|
|
116
|
-
await closeServer(server);
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
});
|