mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-24 02:41:12 +00:00
* 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
144 lines
4.5 KiB
TypeScript
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);
|
|
});
|
|
});
|