mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-26 08:31:55 +00:00
refactor: normalize runtime group sender gating decisions
This commit is contained in:
@@ -30,6 +30,7 @@ import {
|
||||
readChannelAllowFromStore,
|
||||
upsertChannelPairingRequest,
|
||||
} from "../pairing/pairing-store.js";
|
||||
import { evaluateSenderGroupAccessForPolicy } from "../plugin-sdk/group-access.js";
|
||||
import { resolveAgentRoute } from "../routing/resolve-route.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import {
|
||||
@@ -344,23 +345,31 @@ async function shouldProcessLineEvent(
|
||||
return denied;
|
||||
}
|
||||
}
|
||||
if (groupPolicy === "disabled") {
|
||||
if (groupPolicy === "allowlist" && !senderId) {
|
||||
logVerbose("Blocked line group message (no sender ID, groupPolicy: allowlist)");
|
||||
return denied;
|
||||
}
|
||||
const senderGroupAccess = evaluateSenderGroupAccessForPolicy({
|
||||
groupPolicy,
|
||||
groupAllowFrom: effectiveGroupAllow.entries,
|
||||
senderId,
|
||||
isSenderAllowed: (candidateSenderId, allowFrom) =>
|
||||
isSenderAllowed({
|
||||
allow: normalizeAllowFrom(allowFrom),
|
||||
senderId: candidateSenderId,
|
||||
}),
|
||||
});
|
||||
if (!senderGroupAccess.allowed && senderGroupAccess.reason === "disabled") {
|
||||
logVerbose("Blocked line group message (groupPolicy: disabled)");
|
||||
return denied;
|
||||
}
|
||||
if (groupPolicy === "allowlist") {
|
||||
if (!senderId) {
|
||||
logVerbose("Blocked line group message (no sender ID, groupPolicy: allowlist)");
|
||||
return denied;
|
||||
}
|
||||
if (!effectiveGroupAllow.hasEntries) {
|
||||
logVerbose("Blocked line group message (groupPolicy: allowlist, no groupAllowFrom)");
|
||||
return denied;
|
||||
}
|
||||
if (!isSenderAllowed({ allow: effectiveGroupAllow, senderId })) {
|
||||
logVerbose(`Blocked line group message from ${senderId} (groupPolicy: allowlist)`);
|
||||
return denied;
|
||||
}
|
||||
if (!senderGroupAccess.allowed && senderGroupAccess.reason === "empty_allowlist") {
|
||||
logVerbose("Blocked line group message (groupPolicy: allowlist, no groupAllowFrom)");
|
||||
return denied;
|
||||
}
|
||||
if (!senderGroupAccess.allowed && senderGroupAccess.reason === "sender_not_allowlisted") {
|
||||
logVerbose(`Blocked line group message from ${senderId} (groupPolicy: allowlist)`);
|
||||
return denied;
|
||||
}
|
||||
return {
|
||||
allowed: true,
|
||||
|
||||
@@ -1,5 +1,33 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { evaluateSenderGroupAccess } from "./group-access.js";
|
||||
import { evaluateSenderGroupAccess, evaluateSenderGroupAccessForPolicy } from "./group-access.js";
|
||||
|
||||
describe("evaluateSenderGroupAccessForPolicy", () => {
|
||||
it("blocks disabled policy", () => {
|
||||
const decision = evaluateSenderGroupAccessForPolicy({
|
||||
groupPolicy: "disabled",
|
||||
groupAllowFrom: ["123"],
|
||||
senderId: "123",
|
||||
isSenderAllowed: () => true,
|
||||
});
|
||||
|
||||
expect(decision).toMatchObject({ allowed: false, reason: "disabled", groupPolicy: "disabled" });
|
||||
});
|
||||
|
||||
it("blocks allowlist with empty list", () => {
|
||||
const decision = evaluateSenderGroupAccessForPolicy({
|
||||
groupPolicy: "allowlist",
|
||||
groupAllowFrom: [],
|
||||
senderId: "123",
|
||||
isSenderAllowed: () => true,
|
||||
});
|
||||
|
||||
expect(decision).toMatchObject({
|
||||
allowed: false,
|
||||
reason: "empty_allowlist",
|
||||
groupPolicy: "allowlist",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("evaluateSenderGroupAccess", () => {
|
||||
it("defaults missing provider config to allowlist", () => {
|
||||
|
||||
@@ -14,6 +14,48 @@ export type SenderGroupAccessDecision = {
|
||||
reason: SenderGroupAccessReason;
|
||||
};
|
||||
|
||||
export function evaluateSenderGroupAccessForPolicy(params: {
|
||||
groupPolicy: GroupPolicy;
|
||||
providerMissingFallbackApplied?: boolean;
|
||||
groupAllowFrom: string[];
|
||||
senderId: string;
|
||||
isSenderAllowed: (senderId: string, allowFrom: string[]) => boolean;
|
||||
}): SenderGroupAccessDecision {
|
||||
if (params.groupPolicy === "disabled") {
|
||||
return {
|
||||
allowed: false,
|
||||
groupPolicy: params.groupPolicy,
|
||||
providerMissingFallbackApplied: Boolean(params.providerMissingFallbackApplied),
|
||||
reason: "disabled",
|
||||
};
|
||||
}
|
||||
if (params.groupPolicy === "allowlist") {
|
||||
if (params.groupAllowFrom.length === 0) {
|
||||
return {
|
||||
allowed: false,
|
||||
groupPolicy: params.groupPolicy,
|
||||
providerMissingFallbackApplied: Boolean(params.providerMissingFallbackApplied),
|
||||
reason: "empty_allowlist",
|
||||
};
|
||||
}
|
||||
if (!params.isSenderAllowed(params.senderId, params.groupAllowFrom)) {
|
||||
return {
|
||||
allowed: false,
|
||||
groupPolicy: params.groupPolicy,
|
||||
providerMissingFallbackApplied: Boolean(params.providerMissingFallbackApplied),
|
||||
reason: "sender_not_allowlisted",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
allowed: true,
|
||||
groupPolicy: params.groupPolicy,
|
||||
providerMissingFallbackApplied: Boolean(params.providerMissingFallbackApplied),
|
||||
reason: "allowed",
|
||||
};
|
||||
}
|
||||
|
||||
export function evaluateSenderGroupAccess(params: {
|
||||
providerConfigPresent: boolean;
|
||||
configuredGroupPolicy?: GroupPolicy;
|
||||
@@ -28,37 +70,11 @@ export function evaluateSenderGroupAccess(params: {
|
||||
defaultGroupPolicy: params.defaultGroupPolicy,
|
||||
});
|
||||
|
||||
if (groupPolicy === "disabled") {
|
||||
return {
|
||||
allowed: false,
|
||||
groupPolicy,
|
||||
providerMissingFallbackApplied,
|
||||
reason: "disabled",
|
||||
};
|
||||
}
|
||||
if (groupPolicy === "allowlist") {
|
||||
if (params.groupAllowFrom.length === 0) {
|
||||
return {
|
||||
allowed: false,
|
||||
groupPolicy,
|
||||
providerMissingFallbackApplied,
|
||||
reason: "empty_allowlist",
|
||||
};
|
||||
}
|
||||
if (!params.isSenderAllowed(params.senderId, params.groupAllowFrom)) {
|
||||
return {
|
||||
allowed: false,
|
||||
groupPolicy,
|
||||
providerMissingFallbackApplied,
|
||||
reason: "sender_not_allowlisted",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
allowed: true,
|
||||
return evaluateSenderGroupAccessForPolicy({
|
||||
groupPolicy,
|
||||
providerMissingFallbackApplied,
|
||||
reason: "allowed",
|
||||
};
|
||||
groupAllowFrom: params.groupAllowFrom,
|
||||
senderId: params.senderId,
|
||||
isSenderAllowed: params.isSenderAllowed,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -277,6 +277,7 @@ export {
|
||||
} from "./allow-from.js";
|
||||
export {
|
||||
evaluateSenderGroupAccess,
|
||||
evaluateSenderGroupAccessForPolicy,
|
||||
type SenderGroupAccessDecision,
|
||||
type SenderGroupAccessReason,
|
||||
} from "./group-access.js";
|
||||
|
||||
@@ -95,6 +95,7 @@ export {
|
||||
resolveDmGroupAccessWithLists,
|
||||
resolveEffectiveAllowFromLists,
|
||||
} from "../security/dm-policy-shared.js";
|
||||
export { evaluateSenderGroupAccessForPolicy } from "./group-access.js";
|
||||
export type { WizardPrompter } from "../wizard/prompts.js";
|
||||
export { buildAgentMediaPayload } from "./agent-media-payload.js";
|
||||
export { loadOutboundMediaFromUrl } from "./outbound-media.js";
|
||||
|
||||
@@ -91,6 +91,7 @@ export {
|
||||
resolveDmGroupAccessWithLists,
|
||||
resolveEffectiveAllowFromLists,
|
||||
} from "../security/dm-policy-shared.js";
|
||||
export { evaluateSenderGroupAccessForPolicy } from "./group-access.js";
|
||||
export { formatDocsLink } from "../terminal/links.js";
|
||||
export { sleep } from "../utils.js";
|
||||
export { loadWebMedia } from "../web/media.js";
|
||||
|
||||
Reference in New Issue
Block a user