Files
openclaw/extensions/feishu/src/policy.ts
Sid 2972d6fa79 fix(feishu): accept groupPolicy "allowall" as alias for "open" (#36358)
* fix(feishu): accept groupPolicy "allowall" as alias for "open"

When users configure groupPolicy: "allowall" in Feishu channel config,
the Zod schema rejects the value and the runtime policy check falls
through to the allowlist path.  With an empty allowFrom array, all group
messages are silently dropped despite the intended "allow all" semantics.

Accept "allowall" at the schema level (transform to "open") and add a
runtime guard in isFeishuGroupAllowed so the value is handled even if it
bypasses schema validation.

Closes #36312

Made-with: Cursor

* Feishu: tighten allowall alias handling and coverage

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-05 11:32:01 -06:00

125 lines
3.4 KiB
TypeScript

import type {
AllowlistMatch,
ChannelGroupContext,
GroupToolPolicyConfig,
} from "openclaw/plugin-sdk/feishu";
import { normalizeFeishuTarget } from "./targets.js";
import type { FeishuConfig, FeishuGroupConfig } from "./types.js";
export type FeishuAllowlistMatch = AllowlistMatch<"wildcard" | "id">;
function normalizeFeishuAllowEntry(raw: string): string {
const trimmed = raw.trim();
if (!trimmed) {
return "";
}
if (trimmed === "*") {
return "*";
}
const withoutProviderPrefix = trimmed.replace(/^feishu:/i, "");
const normalized = normalizeFeishuTarget(withoutProviderPrefix) ?? withoutProviderPrefix;
return normalized.trim().toLowerCase();
}
export function resolveFeishuAllowlistMatch(params: {
allowFrom: Array<string | number>;
senderId: string;
senderIds?: Array<string | null | undefined>;
senderName?: string | null;
}): FeishuAllowlistMatch {
const allowFrom = params.allowFrom
.map((entry) => normalizeFeishuAllowEntry(String(entry)))
.filter(Boolean);
if (allowFrom.length === 0) {
return { allowed: false };
}
if (allowFrom.includes("*")) {
return { allowed: true, matchKey: "*", matchSource: "wildcard" };
}
// Feishu allowlists are ID-based; mutable display names must never grant access.
const senderCandidates = [params.senderId, ...(params.senderIds ?? [])]
.map((entry) => normalizeFeishuAllowEntry(String(entry ?? "")))
.filter(Boolean);
for (const senderId of senderCandidates) {
if (allowFrom.includes(senderId)) {
return { allowed: true, matchKey: senderId, matchSource: "id" };
}
}
return { allowed: false };
}
export function resolveFeishuGroupConfig(params: {
cfg?: FeishuConfig;
groupId?: string | null;
}): FeishuGroupConfig | undefined {
const groups = params.cfg?.groups ?? {};
const wildcard = groups["*"];
const groupId = params.groupId?.trim();
if (!groupId) {
return undefined;
}
const direct = groups[groupId];
if (direct) {
return direct;
}
const lowered = groupId.toLowerCase();
const matchKey = Object.keys(groups).find((key) => key.toLowerCase() === lowered);
if (matchKey) {
return groups[matchKey];
}
return wildcard;
}
export function resolveFeishuGroupToolPolicy(
params: ChannelGroupContext,
): GroupToolPolicyConfig | undefined {
const cfg = params.cfg.channels?.feishu as FeishuConfig | undefined;
if (!cfg) {
return undefined;
}
const groupConfig = resolveFeishuGroupConfig({
cfg,
groupId: params.groupId,
});
return groupConfig?.tools;
}
export function isFeishuGroupAllowed(params: {
groupPolicy: "open" | "allowlist" | "disabled" | "allowall";
allowFrom: Array<string | number>;
senderId: string;
senderIds?: Array<string | null | undefined>;
senderName?: string | null;
}): boolean {
const { groupPolicy } = params;
if (groupPolicy === "disabled") {
return false;
}
if (groupPolicy === "open" || groupPolicy === "allowall") {
return true;
}
return resolveFeishuAllowlistMatch(params).allowed;
}
export function resolveFeishuReplyPolicy(params: {
isDirectMessage: boolean;
globalConfig?: FeishuConfig;
groupConfig?: FeishuGroupConfig;
}): { requireMention: boolean } {
if (params.isDirectMessage) {
return { requireMention: false };
}
const requireMention =
params.groupConfig?.requireMention ?? params.globalConfig?.requireMention ?? true;
return { requireMention };
}