mirror of https://github.com/openclaw/openclaw.git
148 lines
4.7 KiB
TypeScript
148 lines
4.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
resolveDmAllowState,
|
|
resolveDmGroupAccessDecision,
|
|
resolveEffectiveAllowFromLists,
|
|
} from "./dm-policy-shared.js";
|
|
|
|
describe("security/dm-policy-shared", () => {
|
|
it("normalizes config + store allow entries and counts distinct senders", async () => {
|
|
const state = await resolveDmAllowState({
|
|
provider: "telegram",
|
|
allowFrom: [" * ", " alice ", "ALICE", "bob"],
|
|
normalizeEntry: (value) => value.toLowerCase(),
|
|
readStore: async () => [" Bob ", "carol", ""],
|
|
});
|
|
expect(state.configAllowFrom).toEqual(["*", "alice", "ALICE", "bob"]);
|
|
expect(state.hasWildcard).toBe(true);
|
|
expect(state.allowCount).toBe(3);
|
|
expect(state.isMultiUserDm).toBe(true);
|
|
});
|
|
|
|
it("handles empty allowlists and store failures", async () => {
|
|
const state = await resolveDmAllowState({
|
|
provider: "slack",
|
|
allowFrom: undefined,
|
|
readStore: async () => {
|
|
throw new Error("offline");
|
|
},
|
|
});
|
|
expect(state.configAllowFrom).toEqual([]);
|
|
expect(state.hasWildcard).toBe(false);
|
|
expect(state.allowCount).toBe(0);
|
|
expect(state.isMultiUserDm).toBe(false);
|
|
});
|
|
|
|
it("builds effective DM/group allowlists from config + pairing store", () => {
|
|
const lists = resolveEffectiveAllowFromLists({
|
|
allowFrom: [" owner ", "", "owner2"],
|
|
groupAllowFrom: ["group:abc"],
|
|
storeAllowFrom: [" owner3 ", ""],
|
|
});
|
|
expect(lists.effectiveAllowFrom).toEqual(["owner", "owner2", "owner3"]);
|
|
expect(lists.effectiveGroupAllowFrom).toEqual(["group:abc", "owner3"]);
|
|
});
|
|
|
|
it("falls back to DM allowlist for groups when groupAllowFrom is empty", () => {
|
|
const lists = resolveEffectiveAllowFromLists({
|
|
allowFrom: [" owner "],
|
|
groupAllowFrom: [],
|
|
storeAllowFrom: [" owner2 "],
|
|
});
|
|
expect(lists.effectiveAllowFrom).toEqual(["owner", "owner2"]);
|
|
expect(lists.effectiveGroupAllowFrom).toEqual(["owner", "owner2"]);
|
|
});
|
|
|
|
it("excludes storeAllowFrom when dmPolicy is allowlist", () => {
|
|
const lists = resolveEffectiveAllowFromLists({
|
|
allowFrom: ["+1111"],
|
|
groupAllowFrom: ["group:abc"],
|
|
storeAllowFrom: ["+2222", "+3333"],
|
|
dmPolicy: "allowlist",
|
|
});
|
|
expect(lists.effectiveAllowFrom).toEqual(["+1111"]);
|
|
expect(lists.effectiveGroupAllowFrom).toEqual(["group:abc"]);
|
|
});
|
|
|
|
it("includes storeAllowFrom when dmPolicy is pairing", () => {
|
|
const lists = resolveEffectiveAllowFromLists({
|
|
allowFrom: ["+1111"],
|
|
groupAllowFrom: [],
|
|
storeAllowFrom: ["+2222"],
|
|
dmPolicy: "pairing",
|
|
});
|
|
expect(lists.effectiveAllowFrom).toEqual(["+1111", "+2222"]);
|
|
expect(lists.effectiveGroupAllowFrom).toEqual(["+1111", "+2222"]);
|
|
});
|
|
|
|
const channels = [
|
|
"bluebubbles",
|
|
"imessage",
|
|
"signal",
|
|
"telegram",
|
|
"whatsapp",
|
|
"msteams",
|
|
"matrix",
|
|
"zalo",
|
|
] as const;
|
|
|
|
for (const channel of channels) {
|
|
it(`[${channel}] blocks DM allowlist mode when allowlist is empty`, () => {
|
|
const decision = resolveDmGroupAccessDecision({
|
|
isGroup: false,
|
|
dmPolicy: "allowlist",
|
|
groupPolicy: "allowlist",
|
|
effectiveAllowFrom: [],
|
|
effectiveGroupAllowFrom: [],
|
|
isSenderAllowed: () => false,
|
|
});
|
|
expect(decision).toEqual({
|
|
decision: "block",
|
|
reason: "dmPolicy=allowlist (not allowlisted)",
|
|
});
|
|
});
|
|
|
|
it(`[${channel}] uses pairing flow when DM sender is not allowlisted`, () => {
|
|
const decision = resolveDmGroupAccessDecision({
|
|
isGroup: false,
|
|
dmPolicy: "pairing",
|
|
groupPolicy: "allowlist",
|
|
effectiveAllowFrom: [],
|
|
effectiveGroupAllowFrom: [],
|
|
isSenderAllowed: () => false,
|
|
});
|
|
expect(decision).toEqual({
|
|
decision: "pairing",
|
|
reason: "dmPolicy=pairing (not allowlisted)",
|
|
});
|
|
});
|
|
|
|
it(`[${channel}] allows DM sender when allowlisted`, () => {
|
|
const decision = resolveDmGroupAccessDecision({
|
|
isGroup: false,
|
|
dmPolicy: "allowlist",
|
|
groupPolicy: "allowlist",
|
|
effectiveAllowFrom: ["owner"],
|
|
effectiveGroupAllowFrom: [],
|
|
isSenderAllowed: () => true,
|
|
});
|
|
expect(decision.decision).toBe("allow");
|
|
});
|
|
|
|
it(`[${channel}] blocks group allowlist mode when sender/group is not allowlisted`, () => {
|
|
const decision = resolveDmGroupAccessDecision({
|
|
isGroup: true,
|
|
dmPolicy: "pairing",
|
|
groupPolicy: "allowlist",
|
|
effectiveAllowFrom: ["owner"],
|
|
effectiveGroupAllowFrom: ["group:abc"],
|
|
isSenderAllowed: () => false,
|
|
});
|
|
expect(decision).toEqual({
|
|
decision: "block",
|
|
reason: "groupPolicy=allowlist (not allowlisted)",
|
|
});
|
|
});
|
|
}
|
|
});
|