mirror of https://github.com/openclaw/openclaw.git
test(mattermost): cover monitor auth gates
This commit is contained in:
parent
125e778fe6
commit
1b53918d4f
|
|
@ -0,0 +1,151 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const evaluateSenderGroupAccessForPolicy = vi.hoisted(() => vi.fn());
|
||||
const isDangerousNameMatchingEnabled = vi.hoisted(() => vi.fn());
|
||||
const resolveAllowlistMatchSimple = vi.hoisted(() => vi.fn());
|
||||
const resolveControlCommandGate = vi.hoisted(() => vi.fn());
|
||||
const resolveEffectiveAllowFromLists = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("../runtime-api.js", () => ({
|
||||
evaluateSenderGroupAccessForPolicy,
|
||||
isDangerousNameMatchingEnabled,
|
||||
resolveAllowlistMatchSimple,
|
||||
resolveControlCommandGate,
|
||||
resolveEffectiveAllowFromLists,
|
||||
}));
|
||||
|
||||
describe("mattermost monitor auth", () => {
|
||||
it("normalizes allowlist entries and resolves effective lists", async () => {
|
||||
resolveEffectiveAllowFromLists.mockReturnValue({
|
||||
effectiveAllowFrom: ["alice"],
|
||||
effectiveGroupAllowFrom: ["team"],
|
||||
});
|
||||
|
||||
const {
|
||||
normalizeMattermostAllowEntry,
|
||||
normalizeMattermostAllowList,
|
||||
resolveMattermostEffectiveAllowFromLists,
|
||||
} = await import("./monitor-auth.js");
|
||||
|
||||
expect(normalizeMattermostAllowEntry(" @Alice ")).toBe("alice");
|
||||
expect(normalizeMattermostAllowEntry("mattermost:Bob")).toBe("bob");
|
||||
expect(normalizeMattermostAllowEntry("*")).toBe("*");
|
||||
expect(normalizeMattermostAllowList([" Alice ", "user:alice", "ALICE", "*"])).toEqual([
|
||||
"alice",
|
||||
"*",
|
||||
]);
|
||||
expect(
|
||||
resolveMattermostEffectiveAllowFromLists({
|
||||
allowFrom: [" Alice "],
|
||||
groupAllowFrom: [" Team "],
|
||||
storeAllowFrom: ["Store"],
|
||||
dmPolicy: "pairing",
|
||||
}),
|
||||
).toEqual({
|
||||
effectiveAllowFrom: ["alice"],
|
||||
effectiveGroupAllowFrom: ["team"],
|
||||
});
|
||||
expect(resolveEffectiveAllowFromLists).toHaveBeenCalledWith({
|
||||
allowFrom: ["alice"],
|
||||
groupAllowFrom: ["team"],
|
||||
storeAllowFrom: ["store"],
|
||||
dmPolicy: "pairing",
|
||||
});
|
||||
});
|
||||
|
||||
it("checks sender allowlists against normalized ids and names", async () => {
|
||||
resolveAllowlistMatchSimple.mockReturnValue({ allowed: true });
|
||||
|
||||
const { isMattermostSenderAllowed } = await import("./monitor-auth.js");
|
||||
expect(
|
||||
isMattermostSenderAllowed({
|
||||
senderId: "@Alice",
|
||||
senderName: "Alice",
|
||||
allowFrom: [" mattermost:alice "],
|
||||
allowNameMatching: true,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(resolveAllowlistMatchSimple).toHaveBeenCalledWith({
|
||||
allowFrom: ["alice"],
|
||||
senderId: "alice",
|
||||
senderName: "alice",
|
||||
allowNameMatching: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("authorizes direct messages in open mode and blocks disabled/group-restricted channels", async () => {
|
||||
isDangerousNameMatchingEnabled.mockReturnValue(false);
|
||||
resolveEffectiveAllowFromLists.mockReturnValue({
|
||||
effectiveAllowFrom: [],
|
||||
effectiveGroupAllowFrom: [],
|
||||
});
|
||||
resolveControlCommandGate.mockReturnValue({
|
||||
commandAuthorized: false,
|
||||
shouldBlock: false,
|
||||
});
|
||||
evaluateSenderGroupAccessForPolicy.mockReturnValue({
|
||||
allowed: false,
|
||||
reason: "empty_allowlist",
|
||||
});
|
||||
resolveAllowlistMatchSimple.mockReturnValue({ allowed: false });
|
||||
|
||||
const { authorizeMattermostCommandInvocation } = await import("./monitor-auth.js");
|
||||
|
||||
expect(
|
||||
authorizeMattermostCommandInvocation({
|
||||
account: {
|
||||
config: { dmPolicy: "open" },
|
||||
} as never,
|
||||
cfg: {} as never,
|
||||
senderId: "alice",
|
||||
senderName: "Alice",
|
||||
channelId: "dm-1",
|
||||
channelInfo: { type: "D", name: "alice", display_name: "Alice" } as never,
|
||||
allowTextCommands: false,
|
||||
hasControlCommand: false,
|
||||
}),
|
||||
).toMatchObject({
|
||||
ok: true,
|
||||
commandAuthorized: true,
|
||||
kind: "direct",
|
||||
roomLabel: "#alice",
|
||||
});
|
||||
|
||||
expect(
|
||||
authorizeMattermostCommandInvocation({
|
||||
account: {
|
||||
config: { dmPolicy: "disabled" },
|
||||
} as never,
|
||||
cfg: {} as never,
|
||||
senderId: "alice",
|
||||
senderName: "Alice",
|
||||
channelId: "dm-1",
|
||||
channelInfo: { type: "D", name: "alice", display_name: "Alice" } as never,
|
||||
allowTextCommands: false,
|
||||
hasControlCommand: false,
|
||||
}),
|
||||
).toMatchObject({
|
||||
ok: false,
|
||||
denyReason: "dm-disabled",
|
||||
});
|
||||
|
||||
expect(
|
||||
authorizeMattermostCommandInvocation({
|
||||
account: {
|
||||
config: { groupPolicy: "allowlist" },
|
||||
} as never,
|
||||
cfg: {} as never,
|
||||
senderId: "alice",
|
||||
senderName: "Alice",
|
||||
channelId: "chan-1",
|
||||
channelInfo: { type: "O", name: "town-square", display_name: "Town Square" } as never,
|
||||
allowTextCommands: true,
|
||||
hasControlCommand: false,
|
||||
}),
|
||||
).toMatchObject({
|
||||
ok: false,
|
||||
denyReason: "channel-no-allowlist",
|
||||
kind: "channel",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
evaluateMattermostMentionGate,
|
||||
mapMattermostChannelTypeToChatType,
|
||||
} from "./monitor-gating.js";
|
||||
|
||||
describe("mattermost monitor gating", () => {
|
||||
it("maps mattermost channel types to chat types", () => {
|
||||
expect(mapMattermostChannelTypeToChatType("D")).toBe("direct");
|
||||
expect(mapMattermostChannelTypeToChatType("G")).toBe("group");
|
||||
expect(mapMattermostChannelTypeToChatType("P")).toBe("group");
|
||||
expect(mapMattermostChannelTypeToChatType("O")).toBe("channel");
|
||||
expect(mapMattermostChannelTypeToChatType(undefined)).toBe("channel");
|
||||
});
|
||||
|
||||
it("drops non-mentioned traffic when onchar is enabled but not triggered", () => {
|
||||
const resolveRequireMention = vi.fn(() => true);
|
||||
|
||||
expect(
|
||||
evaluateMattermostMentionGate({
|
||||
kind: "channel",
|
||||
cfg: {} as never,
|
||||
accountId: "default",
|
||||
channelId: "chan-1",
|
||||
resolveRequireMention,
|
||||
wasMentioned: false,
|
||||
isControlCommand: false,
|
||||
commandAuthorized: false,
|
||||
oncharEnabled: true,
|
||||
oncharTriggered: false,
|
||||
canDetectMention: true,
|
||||
}),
|
||||
).toEqual({
|
||||
shouldRequireMention: true,
|
||||
shouldBypassMention: false,
|
||||
effectiveWasMentioned: false,
|
||||
dropReason: "onchar-not-triggered",
|
||||
});
|
||||
});
|
||||
|
||||
it("bypasses mention for authorized control commands and allows direct chats", () => {
|
||||
const resolveRequireMention = vi.fn(() => true);
|
||||
|
||||
expect(
|
||||
evaluateMattermostMentionGate({
|
||||
kind: "channel",
|
||||
cfg: {} as never,
|
||||
accountId: "default",
|
||||
channelId: "chan-1",
|
||||
resolveRequireMention,
|
||||
wasMentioned: false,
|
||||
isControlCommand: true,
|
||||
commandAuthorized: true,
|
||||
oncharEnabled: false,
|
||||
oncharTriggered: false,
|
||||
canDetectMention: true,
|
||||
}),
|
||||
).toEqual({
|
||||
shouldRequireMention: true,
|
||||
shouldBypassMention: true,
|
||||
effectiveWasMentioned: true,
|
||||
dropReason: null,
|
||||
});
|
||||
|
||||
expect(
|
||||
evaluateMattermostMentionGate({
|
||||
kind: "direct",
|
||||
cfg: {} as never,
|
||||
accountId: "default",
|
||||
channelId: "chan-1",
|
||||
resolveRequireMention,
|
||||
wasMentioned: false,
|
||||
isControlCommand: false,
|
||||
commandAuthorized: false,
|
||||
oncharEnabled: false,
|
||||
oncharTriggered: false,
|
||||
canDetectMention: true,
|
||||
}),
|
||||
).toMatchObject({
|
||||
shouldRequireMention: false,
|
||||
dropReason: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue