fix(mcp): block owner-only tools in ACPX bridge

This commit is contained in:
Vincent Koc
2026-04-23 10:22:04 -07:00
parent 91a165c6af
commit 8f3b99c512
3 changed files with 14 additions and 10 deletions

View File

@@ -8,6 +8,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- MCP/tools: stop the ACPX OpenClaw tools bridge from listing or invoking owner-only tools such as `cron`, closing a privilege-escalation path for non-owner MCP callers. (#70698) Thanks @vincentkoc.
- WhatsApp/security: keep contact/vCard/location structured-object free text out of the inline message body and render it through fenced untrusted metadata JSON, limiting hidden prompt-injection payloads in names, phone fields, and location labels/comments.
- Group-chat/security: keep channel-sourced group names and participant labels out of inline group system prompts and render them through fenced untrusted metadata JSON.
- Plugins/startup: restore bundled plugin `openclaw/plugin-sdk/*` resolution from packaged installs and external runtime-deps stage roots, so Telegram/Discord no longer crash-loop with `Cannot find package 'openclaw'` after missing dependency repair.

View File

@@ -3,18 +3,20 @@ import { resolveOpenClawToolsForMcp } from "./openclaw-tools-serve.js";
import { createPluginToolsMcpHandlers } from "./plugin-tools-handlers.js";
describe("OpenClaw tools MCP server", () => {
it("exposes cron", async () => {
it("does not expose owner-only cron", async () => {
const handlers = createPluginToolsMcpHandlers(resolveOpenClawToolsForMcp());
const listed = await handlers.listTools();
expect(listed).toEqual({
tools: [
expect.objectContaining({
name: "cron",
description: expect.stringContaining("Manage Gateway cron jobs"),
inputSchema: expect.objectContaining({ type: "object" }),
}),
],
expect(listed.tools.map((tool) => tool.name)).not.toContain("cron");
});
it("blocks owner-only cron invocation", async () => {
const handlers = createPluginToolsMcpHandlers(resolveOpenClawToolsForMcp());
const result = await handlers.callTool({ name: "cron", arguments: { action: "status" } });
expect(result).toEqual({
content: [{ type: "text", text: "Unknown tool: cron" }],
isError: true,
});
});
});

View File

@@ -19,7 +19,8 @@ function resolveJsonSchemaForTool(tool: AnyAgentTool): Record<string, unknown> {
}
export function createPluginToolsMcpHandlers(tools: AnyAgentTool[]) {
const wrappedTools = tools.map((tool) => {
const allowedTools = tools.filter((tool) => !tool.ownerOnly);
const wrappedTools = allowedTools.map((tool) => {
if (isToolWrappedWithBeforeToolCallHook(tool)) {
return tool;
}