




























@@ -1,8 +1,13 @@
11import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
2233const enqueueSystemEventMock = vi.hoisted(() => vi.fn());
4+type DispatchPluginInteractiveHandlerResult = {
5+matched: boolean;
6+handled: boolean;
7+duplicate: boolean;
8+};
49const dispatchPluginInteractiveHandlerMock = vi.hoisted(() =>
5-vi.fn(async () => ({
10+vi.fn<(arg: unknown) => Promise<DispatchPluginInteractiveHandlerResult>>(async () => ({
611matched: false,
712handled: false,
813duplicate: false,
@@ -171,6 +176,7 @@ function createContext(overrides?: {
171176dmPolicy?: "open" | "allowlist" | "pairing" | "disabled";
172177allowFrom?: string[];
173178allowNameMatching?: boolean;
179+useAccessGroups?: boolean;
174180channelsConfig?: Record<string, { users?: string[] }>;
175181cfg?: Record<string, unknown>;
176182shouldDropMismatchedSlackEvent?: (body: unknown) => boolean;
@@ -249,6 +255,7 @@ function createContext(overrides?: {
249255dmPolicy: overrides?.dmPolicy ?? ("open" as const),
250256allowFrom: overrides?.allowFrom ?? ["*"],
251257allowNameMatching: overrides?.allowNameMatching ?? false,
258+useAccessGroups: overrides?.useAccessGroups ?? true,
252259channelsConfig: overrides?.channelsConfig ?? {},
253260channelsConfigKeys: Object.keys(overrides?.channelsConfig ?? {}),
254261defaultRequireMention: true,
@@ -459,6 +466,9 @@ describe("registerSlackInteractionEvents", () => {
459466conversationId: "C1",
460467interactionId: "U123:C1:100.200:123.trigger:codex:approve:thread-1",
461468threadId: "100.100",
469+auth: expect.objectContaining({
470+isAuthorizedSender: true,
471+}),
462472interaction: expect.objectContaining({
463473actionId: "codex",
464474value: "approve:thread-1",
@@ -472,6 +482,150 @@ describe("registerSlackInteractionEvents", () => {
472482expect(app.client.chat.update).not.toHaveBeenCalled();
473483});
474484485+it("passes false command auth to Slack plugin interactions for non-allowlisted senders", async () => {
486+dispatchPluginInteractiveHandlerMock.mockResolvedValueOnce({
487+matched: true,
488+handled: true,
489+duplicate: false,
490+});
491+const { ctx, getHandler } = createContext({
492+cfg: {
493+commands: {
494+allowFrom: {
495+slack: ["U_OWNER"],
496+},
497+},
498+},
499+});
500+registerSlackInteractionEvents({ ctx: ctx as never });
501+502+const handler = getHandler();
503+expect(handler).toBeTruthy();
504+505+const ack = vi.fn().mockResolvedValue(undefined);
506+await handler!({
507+ ack,
508+body: {
509+user: { id: "U_ALLOWED" },
510+channel: { id: "C1" },
511+container: { channel_id: "C1", message_ts: "100.200", thread_ts: "100.100" },
512+message: {
513+ts: "100.200",
514+text: "fallback",
515+blocks: [
516+{
517+type: "actions",
518+block_id: "codex_actions",
519+elements: [{ type: "button", action_id: "codex" }],
520+},
521+],
522+},
523+},
524+action: {
525+type: "button",
526+action_id: "codex",
527+block_id: "codex_actions",
528+value: "approve:thread-1",
529+},
530+});
531+532+const dispatchCall = dispatchPluginInteractiveHandlerMock.mock.calls[0]?.[0] as
533+| {
534+invoke?: (params: {
535+registration: { handler: (ctx: unknown) => unknown };
536+namespace: string;
537+payload: string;
538+}) => Promise<unknown>;
539+}
540+| undefined;
541+const registrationHandler = vi.fn();
542+await dispatchCall?.invoke?.({
543+registration: { handler: registrationHandler },
544+namespace: "codex",
545+payload: "approve:thread-1",
546+});
547+548+expect(registrationHandler).toHaveBeenCalledWith(
549+expect.objectContaining({
550+auth: expect.objectContaining({
551+isAuthorizedSender: false,
552+}),
553+}),
554+);
555+});
556+557+it("passes true command auth to Slack plugin interactions for allowlisted senders", async () => {
558+dispatchPluginInteractiveHandlerMock.mockResolvedValueOnce({
559+matched: true,
560+handled: true,
561+duplicate: false,
562+});
563+const { ctx, getHandler } = createContext({
564+cfg: {
565+commands: {
566+allowFrom: {
567+slack: ["U_OWNER"],
568+},
569+},
570+},
571+});
572+registerSlackInteractionEvents({ ctx: ctx as never });
573+574+const handler = getHandler();
575+expect(handler).toBeTruthy();
576+577+const ack = vi.fn().mockResolvedValue(undefined);
578+await handler!({
579+ ack,
580+body: {
581+user: { id: "U_OWNER" },
582+channel: { id: "C1" },
583+container: { channel_id: "C1", message_ts: "100.200", thread_ts: "100.100" },
584+message: {
585+ts: "100.200",
586+text: "fallback",
587+blocks: [
588+{
589+type: "actions",
590+block_id: "codex_actions",
591+elements: [{ type: "button", action_id: "codex" }],
592+},
593+],
594+},
595+},
596+action: {
597+type: "button",
598+action_id: "codex",
599+block_id: "codex_actions",
600+value: "approve:thread-1",
601+},
602+});
603+604+const dispatchCall = dispatchPluginInteractiveHandlerMock.mock.calls[0]?.[0] as
605+| {
606+invoke?: (params: {
607+registration: { handler: (ctx: unknown) => unknown };
608+namespace: string;
609+payload: string;
610+}) => Promise<unknown>;
611+}
612+| undefined;
613+const registrationHandler = vi.fn();
614+await dispatchCall?.invoke?.({
615+registration: { handler: registrationHandler },
616+namespace: "codex",
617+payload: "approve:thread-1",
618+});
619+620+expect(registrationHandler).toHaveBeenCalledWith(
621+expect.objectContaining({
622+auth: expect.objectContaining({
623+isAuthorizedSender: true,
624+}),
625+}),
626+);
627+});
628+475629it("treats Slack reply buttons as plain interaction events instead of plugin dispatch", async () => {
476630const { ctx, app, getHandler } = createContext();
477631registerSlackInteractionEvents({ ctx: ctx as never });
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。