惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

I
InfoQ
博客园_首页
美团技术团队
M
MIT News - Artificial intelligence
人人都是产品经理
人人都是产品经理
Blog — PlanetScale
Blog — PlanetScale
H
Help Net Security
J
Java Code Geeks
T
Tailwind CSS Blog
Jina AI
Jina AI
量子位
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
G
Google Developers Blog
爱范儿
爱范儿
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
宝玉的分享
宝玉的分享
小众软件
小众软件
MongoDB | Blog
MongoDB | Blog
博客园 - 三生石上(FineUI控件)
L
LangChain Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
V
Visual Studio Blog
博客园 - Franky
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
Compute plugin callback authorization dynamically [AI] (#...
pgondhi987 · 2026-05-07 · via Recent Commits to openclaw:main

@@ -1,8 +1,13 @@

11

import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";

2233

const enqueueSystemEventMock = vi.hoisted(() => vi.fn());

4+

type DispatchPluginInteractiveHandlerResult = {

5+

matched: boolean;

6+

handled: boolean;

7+

duplicate: boolean;

8+

};

49

const dispatchPluginInteractiveHandlerMock = vi.hoisted(() =>

5-

vi.fn(async () => ({

10+

vi.fn<(arg: unknown) => Promise<DispatchPluginInteractiveHandlerResult>>(async () => ({

611

matched: false,

712

handled: false,

813

duplicate: false,

@@ -171,6 +176,7 @@ function createContext(overrides?: {

171176

dmPolicy?: "open" | "allowlist" | "pairing" | "disabled";

172177

allowFrom?: string[];

173178

allowNameMatching?: boolean;

179+

useAccessGroups?: boolean;

174180

channelsConfig?: Record<string, { users?: string[] }>;

175181

cfg?: Record<string, unknown>;

176182

shouldDropMismatchedSlackEvent?: (body: unknown) => boolean;

@@ -249,6 +255,7 @@ function createContext(overrides?: {

249255

dmPolicy: overrides?.dmPolicy ?? ("open" as const),

250256

allowFrom: overrides?.allowFrom ?? ["*"],

251257

allowNameMatching: overrides?.allowNameMatching ?? false,

258+

useAccessGroups: overrides?.useAccessGroups ?? true,

252259

channelsConfig: overrides?.channelsConfig ?? {},

253260

channelsConfigKeys: Object.keys(overrides?.channelsConfig ?? {}),

254261

defaultRequireMention: true,

@@ -459,6 +466,9 @@ describe("registerSlackInteractionEvents", () => {

459466

conversationId: "C1",

460467

interactionId: "U123:C1:100.200:123.trigger:codex:approve:thread-1",

461468

threadId: "100.100",

469+

auth: expect.objectContaining({

470+

isAuthorizedSender: true,

471+

}),

462472

interaction: expect.objectContaining({

463473

actionId: "codex",

464474

value: "approve:thread-1",

@@ -472,6 +482,150 @@ describe("registerSlackInteractionEvents", () => {

472482

expect(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+475629

it("treats Slack reply buttons as plain interaction events instead of plugin dispatch", async () => {

476630

const { ctx, app, getHandler } = createContext();

477631

registerSlackInteractionEvents({ ctx: ctx as never });