Files
openclaw/src/plugin-sdk/approval-auth-helpers.test.ts
Peter Steinberger 83bf0379c9 refactor(channels): share plugin contract scaffolds (#105838)
* refactor(channels): share plugin contract scaffolds

* fix: preserve channel config hint metadata

* chore: refresh Plugin SDK API baseline

* ci: refresh plugin SDK surface budget

* fix(ci): allow read-only state database boundary

* fix(ci): dedupe read-only state database boundary
2026-07-13 01:51:32 -07:00

144 lines
4.5 KiB
TypeScript

/**
* Tests approval auth helper decisions and implicit same-chat authorization markers.
*/
import { describe, expect, it } from "vitest";
import {
createChannelApprovalAuth,
createResolvedApproverActionAuthAdapter,
isImplicitSameChatApprovalAuthorization,
} from "./approval-auth-helpers.js";
describe("createChannelApprovalAuth", () => {
it("shares resolution, sender checks, and action authorization", () => {
const auth = createChannelApprovalAuth({
channelLabel: "Example",
resolveInputs: () => ({ allowFrom: [" OWNER ", "owner"], defaultTo: "backup" }),
normalizeApprover: (value) => String(value).trim().toLowerCase() || undefined,
});
expect(auth.resolveApprovers({ cfg: {} })).toEqual(["owner", "backup"]);
expect(auth.isAuthorizedSender({ cfg: {}, senderId: "OWNER" })).toBe(true);
expect(
auth.approvalAuth.authorizeActorAction({
cfg: {},
senderId: "attacker",
action: "approve",
approvalKind: "plugin",
}),
).toEqual({
authorized: false,
reason: "❌ You are not authorized to approve plugin requests on Example.",
});
});
it("supports channel-owned wildcard authorization without broadcasting a wildcard target", () => {
const auth = createChannelApprovalAuth({
channelLabel: "Example",
resolveInputs: () => ({ allowFrom: ["*"] }),
normalizeApprover: (value) => (value === "*" ? undefined : String(value)),
isWildcardAuthorized: ({ purpose, senderId, inputs }) =>
purpose === "sender" && Boolean(senderId) && inputs.allowFrom?.includes("*") === true,
});
expect(auth.resolveApprovers({ cfg: {} })).toEqual([]);
expect(auth.isAuthorizedSender({ cfg: {}, senderId: "anyone" })).toBe(true);
expect(auth.isAuthorizedSender({ cfg: {} })).toBe(false);
expect(
auth.approvalAuth.authorizeActorAction({
cfg: {},
senderId: "anyone",
action: "approve",
approvalKind: "exec",
}),
).toEqual({ authorized: true });
});
});
describe("createResolvedApproverActionAuthAdapter", () => {
it.each([
{
name: "falls back to generic same-chat auth when no approvers resolve",
channelLabel: "Slack",
resolveApprovers: () => [],
normalizeSenderId: undefined,
cases: [
{
senderId: "U_OWNER",
approvalKind: "exec" as const,
expected: { authorized: true },
},
],
},
{
name: "allows matching normalized approvers and rejects others",
channelLabel: "Signal",
resolveApprovers: () => ["uuid:owner"],
normalizeSenderId: (value: string) => value.trim().toLowerCase(),
cases: [
{
senderId: " UUID:OWNER ",
approvalKind: "plugin" as const,
expected: { authorized: true },
},
{
senderId: "uuid:attacker",
approvalKind: "plugin" as const,
expected: {
authorized: false,
reason: "❌ You are not authorized to approve plugin requests on Signal.",
},
},
],
},
])("$name", ({ channelLabel, resolveApprovers, normalizeSenderId, cases }) => {
const auth = createResolvedApproverActionAuthAdapter({
channelLabel,
resolveApprovers,
normalizeSenderId,
});
for (const testCase of cases) {
expect(
auth.authorizeActorAction({
cfg: {},
senderId: testCase.senderId,
action: "approve",
approvalKind: testCase.approvalKind,
}),
).toEqual(testCase.expected);
}
});
it("marks empty-approver fallback auth as implicit", () => {
const auth = createResolvedApproverActionAuthAdapter({
channelLabel: "Signal",
resolveApprovers: () => [],
});
const result = auth.authorizeActorAction({
cfg: {},
senderId: "uuid:attacker",
action: "approve",
approvalKind: "exec",
});
expect(result).toEqual({ authorized: true });
expect(isImplicitSameChatApprovalAuthorization(result)).toBe(true);
});
it("does not mark configured-approver auth as implicit", () => {
const auth = createResolvedApproverActionAuthAdapter({
channelLabel: "Signal",
resolveApprovers: () => ["uuid:owner"],
});
const result = auth.authorizeActorAction({
cfg: {},
senderId: "uuid:owner",
action: "approve",
approvalKind: "exec",
});
expect(result).toEqual({ authorized: true });
expect(isImplicitSameChatApprovalAuthorization(result)).toBe(false);
});
});