fix: align open DM allowlist policy (#74112)

* fix: harden telegram open dm allowlist merging

* fix: align open dm allowlist policy
This commit is contained in:
Peter Steinberger
2026-04-29 06:52:12 +01:00
committed by GitHub
parent fda8cc2a9d
commit bd1d1f0f2b
61 changed files with 740 additions and 262 deletions

View File

@@ -338,8 +338,22 @@ vi.mock("openclaw/plugin-sdk/conversation-runtime", async () => {
async function dispatchMessage(params: { cfg: ClawdbotConfig; event: FeishuMessageEvent }) {
const runtime = createRuntimeEnv();
const feishuConfig = params.cfg.channels?.feishu;
const cfg =
feishuConfig?.dmPolicy === "open" && feishuConfig.allowFrom === undefined
? ({
...params.cfg,
channels: {
...params.cfg.channels,
feishu: {
...feishuConfig,
allowFrom: ["*"],
},
},
} as ClawdbotConfig)
: params.cfg;
await handleFeishuMessage({
cfg: params.cfg,
cfg,
event: params.event,
runtime,
});
@@ -637,7 +651,7 @@ describe("handleFeishuMessage command authorization", () => {
expect(mockEnqueueSystemEvent).not.toHaveBeenCalled();
});
it("uses authorizer resolution instead of hardcoded CommandAuthorized=true", async () => {
it("blocks open DMs when a restrictive allowlist does not match", async () => {
const cfg: ClawdbotConfig = {
commands: { useAccessGroups: true },
channels: {
@@ -665,18 +679,8 @@ describe("handleFeishuMessage command authorization", () => {
await dispatchMessage({ cfg, event });
expect(mockResolveCommandAuthorizedFromAuthorizers).toHaveBeenCalledWith({
useAccessGroups: true,
authorizers: [{ configured: true, allowed: false }],
});
expect(mockFinalizeInboundContext).toHaveBeenCalledTimes(1);
expect(mockFinalizeInboundContext).toHaveBeenCalledWith(
expect.objectContaining({
CommandAuthorized: false,
SenderId: "ou-attacker",
Surface: "feishu",
}),
);
expect(mockResolveCommandAuthorizedFromAuthorizers).not.toHaveBeenCalled();
expect(mockFinalizeInboundContext).not.toHaveBeenCalled();
});
it("reads pairing allow store for non-command DMs when dmPolicy is pairing", async () => {
@@ -1610,7 +1614,11 @@ describe("handleFeishuMessage command authorization", () => {
MediaTypes: ["audio/ogg"],
ChatType: "direct",
},
cfg,
cfg: expect.objectContaining({
channels: expect.objectContaining({
feishu: expect.objectContaining({ dmPolicy: "open" }),
}),
}),
});
expect(mockFinalizeInboundContext).toHaveBeenCalledWith(
expect.objectContaining({

View File

@@ -17,6 +17,7 @@ import {
resolveOpenProviderRuntimeGroupPolicy,
warnMissingProviderGroupPolicyFallbackOnce,
} from "openclaw/plugin-sdk/runtime-group-policy";
import { resolveOpenDmAllowlistAccess } from "openclaw/plugin-sdk/security-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { resolveFeishuRuntimeAccount } from "./accounts.js";
import {
@@ -641,9 +642,7 @@ export async function handleFeishuMessage(params: {
cfg,
);
const storeAllowFrom =
!isGroup &&
dmPolicy !== "allowlist" &&
(dmPolicy !== "open" || shouldComputeCommandAuthorized)
!isGroup && dmPolicy !== "allowlist" && dmPolicy !== "open"
? await pairing.readAllowFromStore().catch(() => [])
: [];
const effectiveDmAllowFrom = [...configAllowFrom, ...storeAllowFrom];
@@ -654,7 +653,21 @@ export async function handleFeishuMessage(params: {
senderName: ctx.senderName,
}).allowed;
if (isDirect && dmPolicy !== "open" && !dmAllowed) {
const dmAccessAllowed =
dmPolicy === "open"
? resolveOpenDmAllowlistAccess({
effectiveAllowFrom: effectiveDmAllowFrom,
isSenderAllowed: (allowFrom) =>
resolveFeishuAllowlistMatch({
allowFrom,
senderId: ctx.senderOpenId,
senderIds: [senderUserId],
senderName: ctx.senderName,
}).allowed,
}).decision === "allow"
: dmAllowed;
if (isDirect && !dmAccessAllowed) {
if (dmPolicy === "pairing") {
await pairing.issueChallenge({
senderId: ctx.senderOpenId,

View File

@@ -35,6 +35,7 @@ function buildConfig(overrides?: Partial<ClawdbotConfig>): ClawdbotConfig {
feishu: {
enabled: true,
dmPolicy: "open",
allowFrom: ["*"],
},
},
...overrides,

View File

@@ -1,4 +1,5 @@
import type { ResolvedAgentRoute } from "openclaw/plugin-sdk/routing";
import { resolveOpenDmAllowlistAccess } from "openclaw/plugin-sdk/security-runtime";
import { resolveFeishuRuntimeAccount } from "./accounts.js";
import { createFeishuClient } from "./client.js";
import { createFeishuCommentReplyDispatcher } from "./comment-dispatcher.js";
@@ -96,7 +97,19 @@ export async function handleFeishuCommentEvent(
senderId: turn.senderId,
senderIds: [turn.senderUserId],
}).allowed;
if (dmPolicy !== "open" && !senderAllowed) {
const dmAccessAllowed =
dmPolicy === "open"
? resolveOpenDmAllowlistAccess({
effectiveAllowFrom: effectiveDmAllowFrom,
isSenderAllowed: (allowFrom) =>
resolveFeishuAllowlistMatch({
allowFrom,
senderId: turn.senderId,
senderIds: [turn.senderUserId],
}).allowed,
}).decision === "allow"
: senderAllowed;
if (!dmAccessAllowed) {
if (dmPolicy === "pairing") {
const client = createFeishuClient(account);
await pairing.issueChallenge({