diff --git a/extensions/googlechat/src/monitor-access.ts b/extensions/googlechat/src/monitor-access.ts index bb5b6de9211..0babbc43851 100644 --- a/extensions/googlechat/src/monitor-access.ts +++ b/extensions/googlechat/src/monitor-access.ts @@ -7,6 +7,7 @@ import { resolveDefaultGroupPolicy, resolveDmGroupAccessWithLists, resolveMentionGatingWithBypass, + resolveSenderScopedGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce, } from "openclaw/plugin-sdk/googlechat"; import type { OpenClawConfig } from "openclaw/plugin-sdk/googlechat"; @@ -229,12 +230,10 @@ export async function applyGoogleChatInboundAccessPolicy(params: { const dmPolicy = account.config.dm?.policy ?? "pairing"; const configAllowFrom = (account.config.dm?.allowFrom ?? []).map((v) => String(v)); const normalizedGroupUsers = groupUsers.map((v) => String(v)); - const senderGroupPolicy = - groupPolicy === "disabled" - ? "disabled" - : normalizedGroupUsers.length > 0 - ? "allowlist" - : "open"; + const senderGroupPolicy = resolveSenderScopedGroupPolicy({ + groupPolicy, + groupAllowFrom: normalizedGroupUsers, + }); const shouldComputeAuth = core.channel.commands.shouldComputeCommandAuthorized(rawBody, config); const storeAllowFrom = !isGroup && dmPolicy !== "allowlist" && (dmPolicy !== "open" || shouldComputeAuth) diff --git a/extensions/matrix/src/matrix/monitor/access-policy.ts b/extensions/matrix/src/matrix/monitor/access-policy.ts index 272bc15f0a4..cace7070fd6 100644 --- a/extensions/matrix/src/matrix/monitor/access-policy.ts +++ b/extensions/matrix/src/matrix/monitor/access-policy.ts @@ -3,6 +3,7 @@ import { issuePairingChallenge, readStoreAllowFromForDmPolicy, resolveDmGroupAccessWithLists, + resolveSenderScopedGroupPolicy, } from "openclaw/plugin-sdk/matrix"; import { normalizeMatrixAllowList, @@ -32,12 +33,10 @@ export async function resolveMatrixAccessState(params: { }) : []; const normalizedGroupAllowFrom = normalizeMatrixAllowList(params.groupAllowFrom); - const senderGroupPolicy = - params.groupPolicy === "disabled" - ? "disabled" - : normalizedGroupAllowFrom.length > 0 - ? "allowlist" - : "open"; + const senderGroupPolicy = resolveSenderScopedGroupPolicy({ + groupPolicy: params.groupPolicy, + groupAllowFrom: normalizedGroupAllowFrom, + }); const access = resolveDmGroupAccessWithLists({ isGroup: !params.isDirectMessage, dmPolicy: params.dmPolicy, diff --git a/extensions/msteams/src/monitor-handler/message-handler.ts b/extensions/msteams/src/monitor-handler/message-handler.ts index e4a88eb3b6a..2f14945f65e 100644 --- a/extensions/msteams/src/monitor-handler/message-handler.ts +++ b/extensions/msteams/src/monitor-handler/message-handler.ts @@ -7,6 +7,7 @@ import { createScopedPairingAccess, logInboundDrop, evaluateSenderGroupAccessForPolicy, + resolveSenderScopedGroupPolicy, recordPendingHistoryEntryIfEnabled, resolveControlCommandGate, resolveDefaultGroupPolicy, @@ -175,12 +176,10 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) { conversationId, channelName, }); - const senderGroupPolicy = - groupPolicy === "disabled" - ? "disabled" - : effectiveGroupAllowFrom.length > 0 - ? "allowlist" - : "open"; + const senderGroupPolicy = resolveSenderScopedGroupPolicy({ + groupPolicy, + groupAllowFrom: effectiveGroupAllowFrom, + }); const access = resolveDmGroupAccessWithLists({ isGroup: !isDirectMessage, dmPolicy, diff --git a/src/plugin-sdk/googlechat.ts b/src/plugin-sdk/googlechat.ts index 72c834eb9b5..745fe3bde80 100644 --- a/src/plugin-sdk/googlechat.ts +++ b/src/plugin-sdk/googlechat.ts @@ -73,6 +73,7 @@ export type { WizardPrompter } from "../wizard/prompts.js"; export { resolveInboundRouteEnvelopeBuilderWithRuntime } from "./inbound-envelope.js"; export { createScopedPairingAccess } from "./pairing-access.js"; export { issuePairingChallenge } from "../pairing/pairing-challenge.js"; +export { resolveSenderScopedGroupPolicy } from "./group-access.js"; export { extractToolSend } from "./tool-send.js"; export { resolveWebhookPath } from "./webhook-path.js"; export type { WebhookInFlightLimiter } from "./webhook-request-guards.js"; diff --git a/src/plugin-sdk/group-access.test.ts b/src/plugin-sdk/group-access.test.ts index 135b7b92ff9..81c58b60962 100644 --- a/src/plugin-sdk/group-access.test.ts +++ b/src/plugin-sdk/group-access.test.ts @@ -1,5 +1,35 @@ import { describe, expect, it } from "vitest"; -import { evaluateSenderGroupAccess, evaluateSenderGroupAccessForPolicy } from "./group-access.js"; +import { + evaluateSenderGroupAccess, + evaluateSenderGroupAccessForPolicy, + resolveSenderScopedGroupPolicy, +} from "./group-access.js"; + +describe("resolveSenderScopedGroupPolicy", () => { + it("preserves disabled policy", () => { + expect( + resolveSenderScopedGroupPolicy({ + groupPolicy: "disabled", + groupAllowFrom: ["a"], + }), + ).toBe("disabled"); + }); + + it("maps open/allowlist based on effective sender allowlist", () => { + expect( + resolveSenderScopedGroupPolicy({ + groupPolicy: "allowlist", + groupAllowFrom: ["a"], + }), + ).toBe("allowlist"); + expect( + resolveSenderScopedGroupPolicy({ + groupPolicy: "allowlist", + groupAllowFrom: [], + }), + ).toBe("open"); + }); +}); describe("evaluateSenderGroupAccessForPolicy", () => { it("blocks disabled policy", () => { diff --git a/src/plugin-sdk/group-access.ts b/src/plugin-sdk/group-access.ts index d324c80489b..596549212e3 100644 --- a/src/plugin-sdk/group-access.ts +++ b/src/plugin-sdk/group-access.ts @@ -14,6 +14,16 @@ export type SenderGroupAccessDecision = { reason: SenderGroupAccessReason; }; +export function resolveSenderScopedGroupPolicy(params: { + groupPolicy: GroupPolicy; + groupAllowFrom: string[]; +}): GroupPolicy { + if (params.groupPolicy === "disabled") { + return "disabled"; + } + return params.groupAllowFrom.length > 0 ? "allowlist" : "open"; +} + export function evaluateSenderGroupAccessForPolicy(params: { groupPolicy: GroupPolicy; providerMissingFallbackApplied?: boolean; diff --git a/src/plugin-sdk/index.ts b/src/plugin-sdk/index.ts index f3589732685..68b71a86eb1 100644 --- a/src/plugin-sdk/index.ts +++ b/src/plugin-sdk/index.ts @@ -280,6 +280,7 @@ export { export { evaluateSenderGroupAccess, evaluateSenderGroupAccessForPolicy, + resolveSenderScopedGroupPolicy, type SenderGroupAccessDecision, type SenderGroupAccessReason, } from "./group-access.js"; diff --git a/src/plugin-sdk/matrix.ts b/src/plugin-sdk/matrix.ts index 2e44f819940..fa0551ca56e 100644 --- a/src/plugin-sdk/matrix.ts +++ b/src/plugin-sdk/matrix.ts @@ -93,6 +93,7 @@ export { } from "../security/dm-policy-shared.js"; export { formatDocsLink } from "../terminal/links.js"; export type { WizardPrompter } from "../wizard/prompts.js"; +export { resolveSenderScopedGroupPolicy } from "./group-access.js"; export { createScopedPairingAccess } from "./pairing-access.js"; export { formatResolvedUnresolvedNote } from "./resolution-notes.js"; export { runPluginCommandWithTimeout } from "./run-command.js"; diff --git a/src/plugin-sdk/msteams.ts b/src/plugin-sdk/msteams.ts index 65b6fd21433..2ce1e9eb4a3 100644 --- a/src/plugin-sdk/msteams.ts +++ b/src/plugin-sdk/msteams.ts @@ -91,7 +91,10 @@ export { resolveDmGroupAccessWithLists, resolveEffectiveAllowFromLists, } from "../security/dm-policy-shared.js"; -export { evaluateSenderGroupAccessForPolicy } from "./group-access.js"; +export { + evaluateSenderGroupAccessForPolicy, + resolveSenderScopedGroupPolicy, +} from "./group-access.js"; export { formatDocsLink } from "../terminal/links.js"; export { sleep } from "../utils.js"; export { loadWebMedia } from "../web/media.js";