test: align group policy defaults

This commit is contained in:
Peter Steinberger
2026-01-12 08:45:47 +00:00
parent 7db1cbe178
commit 98e75fce17
7 changed files with 83 additions and 24 deletions

View File

@@ -72,6 +72,7 @@ Clawdbot has two separate “who can trigger me?” layers:
- `whatsapp.groups`, `telegram.groups`, `imessage.groups`: per-group defaults like `requireMention`; when set, it also acts as a group allowlist (include `"*"` to keep allow-all behavior). - `whatsapp.groups`, `telegram.groups`, `imessage.groups`: per-group defaults like `requireMention`; when set, it also acts as a group allowlist (include `"*"` to keep allow-all behavior).
- `groupPolicy="allowlist"` + `groupAllowFrom`: restrict who can trigger the bot *inside* a group session (WhatsApp/Telegram/Signal/iMessage/Microsoft Teams). - `groupPolicy="allowlist"` + `groupAllowFrom`: restrict who can trigger the bot *inside* a group session (WhatsApp/Telegram/Signal/iMessage/Microsoft Teams).
- `discord.guilds` / `slack.channels`: per-surface allowlists + mention defaults. - `discord.guilds` / `slack.channels`: per-surface allowlists + mention defaults.
- **Security note:** treat `dmPolicy="open"` and `groupPolicy="open"` as last-resort settings. They should be barely used; prefer pairing + allowlists unless you fully trust every member of the room.
Details: [Configuration](/gateway/configuration) and [Groups](/concepts/groups) Details: [Configuration](/gateway/configuration) and [Groups](/concepts/groups)

View File

@@ -458,6 +458,7 @@ describe("discord tool result dispatch", () => {
session: { store: "/tmp/clawdbot-sessions.json" }, session: { store: "/tmp/clawdbot-sessions.json" },
discord: { discord: {
dm: { enabled: true, policy: "open" }, dm: { enabled: true, policy: "open" },
groupPolicy: "open",
guilds: { "*": { requireMention: true } }, guilds: { "*": { requireMention: true } },
}, },
messages: { messages: {
@@ -550,6 +551,7 @@ describe("discord tool result dispatch", () => {
messages: { responsePrefix: "PFX" }, messages: { responsePrefix: "PFX" },
discord: { discord: {
dm: { enabled: true, policy: "open" }, dm: { enabled: true, policy: "open" },
groupPolicy: "open",
guilds: { "*": { requireMention: false } }, guilds: { "*": { requireMention: false } },
}, },
} as ReturnType<typeof import("../config/config.js").loadConfig>; } as ReturnType<typeof import("../config/config.js").loadConfig>;
@@ -655,6 +657,7 @@ describe("discord tool result dispatch", () => {
session: { store: "/tmp/clawdbot-sessions.json" }, session: { store: "/tmp/clawdbot-sessions.json" },
discord: { discord: {
dm: { enabled: true, policy: "open" }, dm: { enabled: true, policy: "open" },
groupPolicy: "open",
guilds: { "*": { requireMention: false } }, guilds: { "*": { requireMention: false } },
}, },
routing: { allowFrom: [] }, routing: { allowFrom: [] },
@@ -764,6 +767,7 @@ describe("discord tool result dispatch", () => {
messages: { responsePrefix: "PFX" }, messages: { responsePrefix: "PFX" },
discord: { discord: {
dm: { enabled: true, policy: "open" }, dm: { enabled: true, policy: "open" },
groupPolicy: "open",
guilds: { "*": { requireMention: false } }, guilds: { "*": { requireMention: false } },
}, },
bindings: [ bindings: [

View File

@@ -240,18 +240,30 @@ function buildLiveGatewayConfig(params: {
}): ClawdbotConfig { }): ClawdbotConfig {
const lmstudioProvider = params.cfg.models?.providers?.lmstudio; const lmstudioProvider = params.cfg.models?.providers?.lmstudio;
const baseProviders = params.cfg.models?.providers ?? {}; const baseProviders = params.cfg.models?.providers ?? {};
const nextProviders = { const nextProviders = params.providerOverrides
...baseProviders, ? {
...(lmstudioProvider ...baseProviders,
? { ...(lmstudioProvider
lmstudio: { ? {
...lmstudioProvider, lmstudio: {
api: "openai-completions", ...lmstudioProvider,
}, api: "openai-completions",
} },
: {}), }
...(params.providerOverrides ?? {}), : {}),
}; ...params.providerOverrides,
}
: {
...baseProviders,
...(lmstudioProvider
? {
lmstudio: {
...lmstudioProvider,
api: "openai-completions",
},
}
: {}),
};
const providers = const providers =
Object.keys(nextProviders).length > 0 ? nextProviders : baseProviders; Object.keys(nextProviders).length > 0 ? nextProviders : baseProviders;
return { return {

View File

@@ -129,7 +129,10 @@ describe("monitorIMessageProvider", () => {
it("allows group messages when imessage groups default disables mention gating", async () => { it("allows group messages when imessage groups default disables mention gating", async () => {
config = { config = {
...config, ...config,
imessage: { groups: { "*": { requireMention: false } } }, imessage: {
groupPolicy: "open",
groups: { "*": { requireMention: false } },
},
}; };
const run = monitorIMessageProvider(); const run = monitorIMessageProvider();
await waitForSubscribe(); await waitForSubscribe();
@@ -159,7 +162,10 @@ describe("monitorIMessageProvider", () => {
config = { config = {
...config, ...config,
messages: { groupChat: { mentionPatterns: [] } }, messages: { groupChat: { mentionPatterns: [] } },
imessage: { groups: { "*": { requireMention: true } } }, imessage: {
groupPolicy: "open",
groups: { "*": { requireMention: true } },
},
}; };
const run = monitorIMessageProvider(); const run = monitorIMessageProvider();
await waitForSubscribe(); await waitForSubscribe();

View File

@@ -108,7 +108,10 @@ beforeEach(() => {
ackReaction: "👀", ackReaction: "👀",
ackReactionScope: "group-mentions", ackReactionScope: "group-mentions",
}, },
slack: { dm: { enabled: true, policy: "open", allowFrom: ["*"] } }, slack: {
dm: { enabled: true, policy: "open", allowFrom: ["*"] },
groupPolicy: "open",
},
}; };
sendMock.mockReset().mockResolvedValue(undefined); sendMock.mockReset().mockResolvedValue(undefined);
replyMock.mockReset(); replyMock.mockReset();

View File

@@ -407,7 +407,10 @@ describe("createTelegramBot", () => {
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
identity: { name: "Bert" }, identity: { name: "Bert" },
messages: { groupChat: { mentionPatterns: ["\\bbert\\b"] } }, messages: { groupChat: { mentionPatterns: ["\\bbert\\b"] } },
telegram: { groups: { "*": { requireMention: true } } }, telegram: {
groupPolicy: "open",
groups: { "*": { requireMention: true } },
},
}); });
createTelegramBot({ token: "tok" }); createTelegramBot({ token: "tok" });
@@ -443,7 +446,10 @@ describe("createTelegramBot", () => {
replySpy.mockReset(); replySpy.mockReset();
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
telegram: { groups: { "*": { requireMention: false } } }, telegram: {
groupPolicy: "open",
groups: { "*": { requireMention: false } },
},
}); });
createTelegramBot({ token: "tok" }); createTelegramBot({ token: "tok" });
@@ -489,7 +495,10 @@ describe("createTelegramBot", () => {
ackReactionScope: "group-mentions", ackReactionScope: "group-mentions",
groupChat: { mentionPatterns: ["\\bbert\\b"] }, groupChat: { mentionPatterns: ["\\bbert\\b"] },
}, },
telegram: { groups: { "*": { requireMention: true } } }, telegram: {
groupPolicy: "open",
groups: { "*": { requireMention: true } },
},
}); });
createTelegramBot({ token: "tok" }); createTelegramBot({ token: "tok" });
@@ -533,7 +542,10 @@ describe("createTelegramBot", () => {
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
messages: { groupChat: { mentionPatterns: ["\\bbert\\b"] } }, messages: { groupChat: { mentionPatterns: ["\\bbert\\b"] } },
telegram: { groups: { "*": { requireMention: true } } }, telegram: {
groupPolicy: "open",
groups: { "*": { requireMention: true } },
},
}); });
createTelegramBot({ token: "tok" }); createTelegramBot({ token: "tok" });
@@ -565,7 +577,10 @@ describe("createTelegramBot", () => {
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
messages: { groupChat: { mentionPatterns: [] } }, messages: { groupChat: { mentionPatterns: [] } },
telegram: { groups: { "*": { requireMention: true } } }, telegram: {
groupPolicy: "open",
groups: { "*": { requireMention: true } },
},
}); });
createTelegramBot({ token: "tok" }); createTelegramBot({ token: "tok" });
@@ -838,7 +853,10 @@ describe("createTelegramBot", () => {
"utf-8", "utf-8",
); );
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
telegram: { groups: { "*": { requireMention: true } } }, telegram: {
groupPolicy: "open",
groups: { "*": { requireMention: true } },
},
bindings: [ bindings: [
{ {
agentId: "ops", agentId: "ops",
@@ -877,6 +895,7 @@ describe("createTelegramBot", () => {
replySpy.mockReset(); replySpy.mockReset();
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
telegram: { telegram: {
groupPolicy: "open",
groups: { groups: {
"*": { requireMention: true }, "*": { requireMention: true },
"123": { requireMention: false }, "123": { requireMention: false },
@@ -910,6 +929,7 @@ describe("createTelegramBot", () => {
replySpy.mockReset(); replySpy.mockReset();
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
telegram: { telegram: {
groupPolicy: "open",
groups: { groups: {
"*": { requireMention: true }, "*": { requireMention: true },
"-1001234567890": { "-1001234567890": {
@@ -954,6 +974,7 @@ describe("createTelegramBot", () => {
replySpy.mockReset(); replySpy.mockReset();
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
telegram: { telegram: {
groupPolicy: "open",
groups: { "*": { requireMention: false } }, groups: { "*": { requireMention: false } },
}, },
}); });
@@ -983,7 +1004,10 @@ describe("createTelegramBot", () => {
>; >;
replySpy.mockReset(); replySpy.mockReset();
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
telegram: { groups: { "*": { requireMention: true } } }, telegram: {
groupPolicy: "open",
groups: { "*": { requireMention: true } },
},
}); });
createTelegramBot({ token: "tok" }); createTelegramBot({ token: "tok" });
@@ -1610,7 +1634,10 @@ describe("createTelegramBot", () => {
replySpy.mockReset(); replySpy.mockReset();
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
telegram: { groups: { "*": { requireMention: false } } }, telegram: {
groupPolicy: "open",
groups: { "*": { requireMention: false } },
},
}); });
createTelegramBot({ token: "tok" }); createTelegramBot({ token: "tok" });
@@ -1658,6 +1685,7 @@ describe("createTelegramBot", () => {
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
telegram: { telegram: {
groupPolicy: "open",
groups: { groups: {
"-1001234567890": { "-1001234567890": {
requireMention: false, requireMention: false,
@@ -1715,7 +1743,10 @@ describe("createTelegramBot", () => {
replySpy.mockResolvedValue({ text: "response" }); replySpy.mockResolvedValue({ text: "response" });
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
telegram: { groups: { "*": { requireMention: false } } }, telegram: {
groupPolicy: "open",
groups: { "*": { requireMention: false } },
},
}); });
createTelegramBot({ token: "tok" }); createTelegramBot({ token: "tok" });

View File

@@ -682,6 +682,7 @@ describe("web monitor inbox", () => {
mockLoadConfig.mockReturnValue({ mockLoadConfig.mockReturnValue({
whatsapp: { whatsapp: {
allowFrom: ["+111"], // does not include +777 allowFrom: ["+111"], // does not include +777
groupPolicy: "open",
}, },
messages: { messages: {
messagePrefix: undefined, messagePrefix: undefined,
@@ -847,6 +848,7 @@ describe("web monitor inbox", () => {
mockLoadConfig.mockReturnValue({ mockLoadConfig.mockReturnValue({
whatsapp: { whatsapp: {
allowFrom: ["+1234"], allowFrom: ["+1234"],
groupPolicy: "open",
}, },
messages: { messages: {
messagePrefix: undefined, messagePrefix: undefined,