UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

370 lines (369 loc) • 18.2 kB
import { r as asNullableRecord } from "./record-coerce-TIsquoVR.mjs"; import { d as normalizeStringEntries, y as uniqueStrings } from "./string-normalization-CECOqf64.mjs"; import { n as sanitizeForLog } from "./ansi-BeFJ0Uu7.mjs"; import { a as parseAccessGroupAllowFromEntry } from "./allow-from-C_5BIb-Y.mjs"; import "./group-policy-BJ7vM7Bm.mjs"; import { s as createScopedDmSecurityResolver } from "./channel-config-helpers-BApaQcwN.mjs"; import "./helpers-Bm4RKejp.mjs"; import { t as collectProviderDangerousNameMatchingScopes } from "./dangerous-name-matching-RaIYI9ah.mjs"; import "./channel-access-compat-pm7PEBtW.mjs"; import { i as resolveOpenProviderRuntimeGroupPolicy, n as resolveAllowlistProviderRuntimeGroupPolicy, r as resolveDefaultGroupPolicy } from "./runtime-group-policy-b76VJ3T3.mjs"; //#region src/channels/plugins/group-policy-warnings.ts function composeWarningCollectors(...collectors) { return (params) => collectors.flatMap((collector) => collector?.(params) ?? []); } function projectWarningCollector(project, collector) { return (params) => collector(project(params)); } function projectConfigWarningCollector(collector) { return projectWarningCollector((params) => ({ cfg: params.cfg }), collector); } function projectConfigAccountIdWarningCollector(collector) { return projectWarningCollector((params) => ({ cfg: params.cfg, accountId: params.accountId }), collector); } function projectAccountWarningCollector(collector) { return projectWarningCollector((params) => params.account, collector); } function projectAccountConfigWarningCollector(projectCfg, collector) { return projectWarningCollector((params) => ({ account: params.account, cfg: projectCfg(params.cfg) }), collector); } function createSecurityAuditFindingCollector(options) { return (params) => options.collectWarnings(params).map((message) => message.trim()).filter(Boolean).map((message) => ({ checkId: options.checkId, severity: options.severity, title: options.title, detail: message.replace(/^-\s*/, "") })); } const createConditionalWarningCollector = Object.assign((...collectors) => (params) => collectors.flatMap((collector) => { const next = collector(params); if (!next) return []; return Array.isArray(next) ? next : [next]; }), { findings: createSecurityAuditFindingCollector }); function composeAccountWarningCollectors(baseCollector, ...collectors) { return composeWarningCollectors(baseCollector, createConditionalWarningCollector(...collectors.map((collector) => ({ account }) => collector(account)))); } function buildOpenGroupPolicyWarning(params) { return `- ${params.surface}: groupPolicy="open" ${params.openBehavior}. ${params.remediation}.`; } function buildOpenGroupPolicyRestrictSendersWarning(params) { const mentionSuffix = params.mentionGated === false ? "" : " (mention-gated)"; return buildOpenGroupPolicyWarning({ surface: params.surface, openBehavior: `allows ${params.openScope} to trigger${mentionSuffix}`, remediation: `Set ${params.groupPolicyPath}="allowlist" + ${params.groupAllowFromPath} to restrict senders` }); } function buildOpenGroupPolicyNoRouteAllowlistWarning(params) { const mentionSuffix = params.mentionGated === false ? "" : " (mention-gated)"; return buildOpenGroupPolicyWarning({ surface: params.surface, openBehavior: `with no ${params.routeAllowlistPath} allowlist; any ${params.routeScope} can add + ping${mentionSuffix}`, remediation: `Set ${params.groupPolicyPath}="allowlist" + ${params.groupAllowFromPath} or configure ${params.routeAllowlistPath}` }); } function buildOpenGroupPolicyConfigureRouteAllowlistWarning(params) { const mentionSuffix = params.mentionGated === false ? "" : " (mention-gated)"; return buildOpenGroupPolicyWarning({ surface: params.surface, openBehavior: `allows ${params.openScope} to trigger${mentionSuffix}`, remediation: `Set ${params.groupPolicyPath}="allowlist" and configure ${params.routeAllowlistPath}` }); } function collectOpenGroupPolicyRestrictSendersWarnings(params) { if (params.groupPolicy !== "open") return []; return [buildOpenGroupPolicyRestrictSendersWarning(params)]; } function collectAllowlistProviderRestrictSendersWarnings(params) { return collectAllowlistProviderGroupPolicyWarnings({ ...params, collect: (groupPolicy) => collectOpenGroupPolicyRestrictSendersWarnings({ ...params, groupPolicy }) }); } /** Build an account-aware allowlist-provider warning collector for sender-restricted groups. */ function createAllowlistProviderRestrictSendersWarningCollector(params) { return createAllowlistProviderGroupPolicyWarningCollector({ providerConfigPresent: params.providerConfigPresent, resolveGroupPolicy: ({ account }) => params.resolveGroupPolicy(account), collect: ({ groupPolicy }) => collectOpenGroupPolicyRestrictSendersWarnings({ ...params, groupPolicy }) }); } /** Build a direct account-aware warning collector when the policy already lives on the account. */ function createOpenGroupPolicyRestrictSendersWarningCollector(params) { return (account) => collectOpenGroupPolicyRestrictSendersWarnings({ ...params, groupPolicy: params.resolveGroupPolicy(account) ?? params.defaultGroupPolicy ?? "allowlist" }); } function createProviderGroupPolicyWarningCollectors(resolvePolicy) { const collectWarnings = (params) => params.collect(resolvePolicy({ providerConfigPresent: params.providerConfigPresent, groupPolicy: params.configuredGroupPolicy ?? void 0, defaultGroupPolicy: resolveDefaultGroupPolicy(params.cfg) }).groupPolicy); const createCollector = (params) => (runtime) => collectWarnings({ cfg: runtime.cfg, providerConfigPresent: params.providerConfigPresent(runtime.cfg), configuredGroupPolicy: params.resolveGroupPolicy(runtime), collect: (groupPolicy) => params.collect({ ...runtime, groupPolicy }) }); return { collectWarnings, createCollector }; } const { collectWarnings: collectAllowlistProviderGroupPolicyWarnings, createCollector: createAllowlistProviderGroupPolicyWarningCollector } = createProviderGroupPolicyWarningCollectors(resolveAllowlistProviderRuntimeGroupPolicy); const { collectWarnings: collectOpenProviderGroupPolicyWarnings, createCollector: createOpenProviderGroupPolicyWarningCollector } = createProviderGroupPolicyWarningCollectors(resolveOpenProviderRuntimeGroupPolicy); /** Build an account-aware allowlist-provider warning collector for simple open-policy warnings. */ function createAllowlistProviderOpenWarningCollector(params) { return createAllowlistProviderGroupPolicyWarningCollector({ providerConfigPresent: params.providerConfigPresent, resolveGroupPolicy: ({ account }) => params.resolveGroupPolicy(account), collect: ({ groupPolicy }) => groupPolicy === "open" ? [buildOpenGroupPolicyWarning(params.buildOpenWarning)] : [] }); } function collectOpenGroupPolicyRouteAllowlistWarnings(params) { if (params.groupPolicy !== "open") return []; if (params.routeAllowlistConfigured) return [buildOpenGroupPolicyRestrictSendersWarning(params.restrictSenders)]; return [buildOpenGroupPolicyNoRouteAllowlistWarning(params.noRouteAllowlist)]; } /** Build an account-aware allowlist-provider warning collector for route-allowlisted groups. */ function createAllowlistProviderRouteAllowlistWarningCollector(params) { return createAllowlistProviderGroupPolicyWarningCollector({ providerConfigPresent: params.providerConfigPresent, resolveGroupPolicy: ({ account }) => params.resolveGroupPolicy(account), collect: ({ account, groupPolicy }) => collectOpenGroupPolicyRouteAllowlistWarnings({ groupPolicy, routeAllowlistConfigured: params.resolveRouteAllowlistConfigured(account), restrictSenders: params.restrictSenders, noRouteAllowlist: params.noRouteAllowlist }) }); } function collectOpenGroupPolicyConfiguredRouteWarnings(params) { if (params.groupPolicy !== "open") return []; if (params.routeAllowlistConfigured) return [buildOpenGroupPolicyConfigureRouteAllowlistWarning(params.configureRouteAllowlist)]; return [buildOpenGroupPolicyWarning(params.missingRouteAllowlist)]; } /** Build an account-aware open-provider warning collector for configured-route channels. */ function createOpenProviderConfiguredRouteWarningCollector(params) { return createOpenProviderGroupPolicyWarningCollector({ providerConfigPresent: params.providerConfigPresent, resolveGroupPolicy: ({ account }) => params.resolveGroupPolicy(account), collect: ({ account, groupPolicy }) => collectOpenGroupPolicyConfiguredRouteWarnings({ groupPolicy, routeAllowlistConfigured: params.resolveRouteAllowlistConfigured(account), configureRouteAllowlist: params.configureRouteAllowlist, missingRouteAllowlist: params.missingRouteAllowlist }) }); } //#endregion //#region src/plugin-sdk/channel-policy.ts /** @deprecated Use `resolveChannelMessageIngress` from `openclaw/plugin-sdk/channel-ingress-runtime`. */ function resolveSenderScopedGroupPolicy(params) { if (params.groupPolicy === "disabled") return "disabled"; return params.groupAllowFrom.length > 0 ? "allowlist" : "open"; } /** @deprecated Use route descriptors with `resolveChannelMessageIngress` from `openclaw/plugin-sdk/channel-ingress-runtime`. */ function evaluateGroupRouteAccessForPolicy(params) { if (params.groupPolicy === "disabled") return { allowed: false, groupPolicy: params.groupPolicy, reason: "disabled" }; if (params.routeMatched && params.routeEnabled === false) return { allowed: false, groupPolicy: params.groupPolicy, reason: "route_disabled" }; if (params.groupPolicy === "allowlist") { if (!params.routeAllowlistConfigured) return { allowed: false, groupPolicy: params.groupPolicy, reason: "empty_allowlist" }; if (!params.routeMatched) return { allowed: false, groupPolicy: params.groupPolicy, reason: "route_not_allowlisted" }; } return { allowed: true, groupPolicy: params.groupPolicy, reason: "allowed" }; } /** @deprecated Use `resolveChannelMessageIngress` from `openclaw/plugin-sdk/channel-ingress-runtime`. */ function evaluateSenderGroupAccessForPolicy(params) { const providerMissingFallbackApplied = Boolean(params.providerMissingFallbackApplied); let reason = "allowed"; if (params.groupPolicy === "disabled") reason = "disabled"; else if (params.groupPolicy === "allowlist") { if (params.groupAllowFrom.length === 0) reason = "empty_allowlist"; else if (!params.isSenderAllowed(params.senderId, params.groupAllowFrom)) reason = "sender_not_allowlisted"; } return { allowed: reason === "allowed", groupPolicy: params.groupPolicy, providerMissingFallbackApplied, reason }; } /** Normalizes allowFrom entries into trimmed unique string identifiers. */ function normalizeAllowFromList(list) { if (!Array.isArray(list)) return []; return normalizeStringEntries(list); } /** Coerces native feature settings to the supported boolean/auto shape. */ function coerceNativeSetting(value) { if (value === true || value === false || value === "auto") return value; } /** Collect the common account, nested-DM, and group/room allowlist paths for doctor warnings. */ function collectStandardAllowlistLists(scope, options = {}) { const lists = []; if (options.includeAllowFrom !== false) lists.push({ pathLabel: `${scope.prefix}.allowFrom`, list: scope.account.allowFrom }); if (options.includeGroupAllowFrom !== false) lists.push({ pathLabel: `${scope.prefix}.groupAllowFrom`, list: scope.account.groupAllowFrom }); if (options.includeDm) { const dm = asNullableRecord(scope.account.dm); if (dm) lists.push({ pathLabel: `${scope.prefix}.dm.allowFrom`, list: dm.allowFrom }); } if (options.includeGroups) { const groupsKey = options.groupsKey ?? "groups"; const groupField = options.groupField ?? "allowFrom"; const groups = asNullableRecord(scope.account[groupsKey]); if (groups) for (const [groupKey, groupRaw] of Object.entries(groups)) { const group = asNullableRecord(groupRaw); if (!group) continue; lists.push({ pathLabel: `${scope.prefix}.${groupsKey}.${groupKey}.${groupField}`, list: group[groupField] }); } } return lists; } function stripMutableAllowEntryPrefixes(value, prefixes) { let current = value; for (;;) { const prefix = prefixes.find((candidate) => current.slice(0, candidate.length).toLowerCase() === candidate.toLowerCase()); if (prefix === void 0) return current; current = current.slice(prefix.length).trim(); } } /** Build a mutable-name detector by stripping channel prefixes and recognizing stable IDs. */ function buildMutableAllowEntryDetector(params) { const prefixes = (params.prefixes ?? []).filter((prefix) => prefix.length > 0); return (entry) => { const text = entry.trim(); if (!text || text === "*" || parseAccessGroupAllowFromEntry(text) !== null) return false; const normalized = stripMutableAllowEntryPrefixes(text, prefixes); if (!normalized) return false; params.stableIdPattern.lastIndex = 0; return !params.stableIdPattern.test(normalized); }; } function collectMutableAllowlistWarningLines(hits, channel) { if (hits.length === 0) return []; const exampleLines = hits.slice(0, 8).map((hit) => `- ${sanitizeForLog(hit.path)}: ${sanitizeForLog(hit.entry)}`); const remaining = hits.length > 8 ? `- +${hits.length - 8} more mutable allowlist entries.` : null; const flagPaths = uniqueStrings(hits.map((hit) => hit.dangerousFlagPath)); const flagHint = flagPaths.length === 1 ? sanitizeForLog(flagPaths[0] ?? "") : `${sanitizeForLog(flagPaths[0] ?? "")} (and ${flagPaths.length - 1} other scope flags)`; return [ `- Found ${hits.length} mutable allowlist ${hits.length === 1 ? "entry" : "entries"} across ${channel} while name matching is disabled by default.`, ...exampleLines, ...remaining ? [remaining] : [], `- Option A (break-glass): enable ${flagHint}=true to keep name/email/nick matching.`, "- Option B (recommended): resolve names/emails/nicks to stable sender IDs and rewrite the allowlist entries." ]; } /** * Create a warning collector for mutable name/email/nick allowlists while stable-id matching is required. * Channel plugins provide a detector for entries that depend on dangerous name matching. */ function createDangerousNameMatchingMutableAllowlistWarningCollector(params) { return ({ cfg }) => { const hits = []; for (const scope of collectProviderDangerousNameMatchingScopes(cfg, params.channel)) { if (scope.dangerousNameMatchingEnabled) continue; for (const candidate of params.collectLists(scope)) { if (!Array.isArray(candidate.list)) continue; for (const entry of candidate.list) { const text = String(entry).trim(); if (!text || text === "*" || !params.detector(text)) continue; hits.push({ path: candidate.pathLabel, entry: text, dangerousFlagPath: scope.dangerousFlagPath }); } } } return collectMutableAllowlistWarningLines(hits, params.channel); }; } /** * Compose the common account-scoped DM policy resolver with restrict-senders group warnings. * This is the shared adapter shape for channels whose DM security and group policy live together. */ function createRestrictSendersChannelSecurity(params) { const collectOpenGroupFindings = createConditionalWarningCollector.findings({ collectWarnings: createAllowlistProviderRestrictSendersWarningCollector({ providerConfigPresent: params.providerConfigPresent ?? ((cfg) => cfg.channels?.[params.channelKey] !== void 0), resolveGroupPolicy: params.resolveGroupPolicy, surface: params.surface, openScope: params.openScope, groupPolicyPath: params.groupPolicyPath, groupAllowFromPath: params.groupAllowFromPath, mentionGated: params.mentionGated }), checkId: `channels.${params.channelKey}.groups.open`, severity: "warn", title: params.findingTitle ?? `${params.surface} security warning` }); return { resolveDmPolicy: createScopedDmSecurityResolver({ channelKey: params.channelKey, resolvePolicy: params.resolveDmPolicy, resolveAllowFrom: params.resolveDmAllowFrom, resolveFallbackAccountId: params.resolveFallbackAccountId, defaultPolicy: params.defaultDmPolicy, allowFromPathSuffix: params.allowFromPathSuffix, policyPathSuffix: params.policyPathSuffix, approveChannelId: params.approveChannelId, approveHint: params.approveHint, normalizeEntry: params.normalizeDmEntry, classifyEntryAuthentication: params.classifyEntryAuthentication, inheritSharedDefaultsFromDefaultAccount: params.inheritSharedDefaultsFromDefaultAccount }), ...params.dmRouting ? { dmRouting: params.dmRouting } : {}, collectWarnings: collectOpenGroupFindings }; } //#endregion export { projectAccountWarningCollector as A, createAllowlistProviderRestrictSendersWarningCollector as C, createOpenProviderConfiguredRouteWarningCollector as D, createOpenGroupPolicyRestrictSendersWarningCollector as E, projectConfigWarningCollector as M, projectWarningCollector as N, createOpenProviderGroupPolicyWarningCollector as O, createAllowlistProviderOpenWarningCollector as S, createConditionalWarningCollector as T, collectOpenGroupPolicyRouteAllowlistWarnings as _, createRestrictSendersChannelSecurity as a, composeWarningCollectors as b, normalizeAllowFromList as c, buildOpenGroupPolicyRestrictSendersWarning as d, buildOpenGroupPolicyWarning as f, collectOpenGroupPolicyRestrictSendersWarnings as g, collectOpenGroupPolicyConfiguredRouteWarnings as h, createDangerousNameMatchingMutableAllowlistWarningCollector as i, projectConfigAccountIdWarningCollector as j, projectAccountConfigWarningCollector as k, resolveSenderScopedGroupPolicy as l, collectAllowlistProviderRestrictSendersWarnings as m, coerceNativeSetting as n, evaluateGroupRouteAccessForPolicy as o, collectAllowlistProviderGroupPolicyWarnings as p, collectStandardAllowlistLists as r, evaluateSenderGroupAccessForPolicy as s, buildMutableAllowEntryDetector as t, buildOpenGroupPolicyConfigureRouteAllowlistWarning as u, collectOpenProviderGroupPolicyWarnings as v, createAllowlistProviderRouteAllowlistWarningCollector as w, createAllowlistProviderGroupPolicyWarningCollector as x, composeAccountWarningCollectors as y };