refactor: normalize runtime group sender gating decisions

This commit is contained in:
Peter Steinberger
2026-03-07 21:50:19 +00:00
parent 5eba663c38
commit 27dad962fe
8 changed files with 190 additions and 109 deletions

View File

@@ -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", () => {

View File

@@ -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,
});
}

View File

@@ -277,6 +277,7 @@ export {
} from "./allow-from.js";
export {
evaluateSenderGroupAccess,
evaluateSenderGroupAccessForPolicy,
type SenderGroupAccessDecision,
type SenderGroupAccessReason,
} from "./group-access.js";

View File

@@ -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";

View File

@@ -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";