mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 05:01:23 +00:00
fix: harden Feishu media URL fetching (#16285) (thanks @mbelinky)
Security fix for Feishu extension media fetching.
This commit is contained in:
committed by
GitHub
parent
d82c5ea9d1
commit
5b4121d601
123
extensions/feishu/src/docx.test.ts
Normal file
123
extensions/feishu/src/docx.test.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const createFeishuClientMock = vi.hoisted(() => vi.fn());
|
||||
const fetchRemoteMediaMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("./client.js", () => ({
|
||||
createFeishuClient: createFeishuClientMock,
|
||||
}));
|
||||
|
||||
vi.mock("./runtime.js", () => ({
|
||||
getFeishuRuntime: () => ({
|
||||
channel: {
|
||||
media: {
|
||||
fetchRemoteMedia: fetchRemoteMediaMock,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
import { registerFeishuDocTools } from "./docx.js";
|
||||
|
||||
describe("feishu_doc image fetch hardening", () => {
|
||||
const convertMock = vi.hoisted(() => vi.fn());
|
||||
const blockListMock = vi.hoisted(() => vi.fn());
|
||||
const blockChildrenCreateMock = vi.hoisted(() => vi.fn());
|
||||
const driveUploadAllMock = vi.hoisted(() => vi.fn());
|
||||
const blockPatchMock = vi.hoisted(() => vi.fn());
|
||||
const scopeListMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
createFeishuClientMock.mockReturnValue({
|
||||
docx: {
|
||||
document: {
|
||||
convert: convertMock,
|
||||
},
|
||||
documentBlock: {
|
||||
list: blockListMock,
|
||||
patch: blockPatchMock,
|
||||
},
|
||||
documentBlockChildren: {
|
||||
create: blockChildrenCreateMock,
|
||||
},
|
||||
},
|
||||
drive: {
|
||||
media: {
|
||||
uploadAll: driveUploadAllMock,
|
||||
},
|
||||
},
|
||||
application: {
|
||||
scope: {
|
||||
list: scopeListMock,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
convertMock.mockResolvedValue({
|
||||
code: 0,
|
||||
data: {
|
||||
blocks: [{ block_type: 27 }],
|
||||
first_level_block_ids: [],
|
||||
},
|
||||
});
|
||||
|
||||
blockListMock.mockResolvedValue({
|
||||
code: 0,
|
||||
data: {
|
||||
items: [],
|
||||
},
|
||||
});
|
||||
|
||||
blockChildrenCreateMock.mockResolvedValue({
|
||||
code: 0,
|
||||
data: {
|
||||
children: [{ block_type: 27, block_id: "img_block_1" }],
|
||||
},
|
||||
});
|
||||
|
||||
driveUploadAllMock.mockResolvedValue({ file_token: "token_1" });
|
||||
blockPatchMock.mockResolvedValue({ code: 0 });
|
||||
scopeListMock.mockResolvedValue({ code: 0, data: { scopes: [] } });
|
||||
});
|
||||
|
||||
it("skips image upload when markdown image URL is blocked", async () => {
|
||||
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
fetchRemoteMediaMock.mockRejectedValueOnce(
|
||||
new Error("Blocked: resolves to private/internal IP address"),
|
||||
);
|
||||
|
||||
const registerTool = vi.fn();
|
||||
registerFeishuDocTools({
|
||||
config: {
|
||||
channels: {
|
||||
feishu: {
|
||||
appId: "app_id",
|
||||
appSecret: "app_secret",
|
||||
},
|
||||
},
|
||||
} as any,
|
||||
logger: { debug: vi.fn(), info: vi.fn() } as any,
|
||||
registerTool,
|
||||
} as any);
|
||||
|
||||
const feishuDocTool = registerTool.mock.calls
|
||||
.map((call) => call[0])
|
||||
.find((tool) => tool.name === "feishu_doc");
|
||||
expect(feishuDocTool).toBeDefined();
|
||||
|
||||
const result = await feishuDocTool.execute("tool-call", {
|
||||
action: "write",
|
||||
doc_token: "doc_1",
|
||||
content: "",
|
||||
});
|
||||
|
||||
expect(fetchRemoteMediaMock).toHaveBeenCalled();
|
||||
expect(driveUploadAllMock).not.toHaveBeenCalled();
|
||||
expect(blockPatchMock).not.toHaveBeenCalled();
|
||||
expect(result.details.images_processed).toBe(0);
|
||||
expect(consoleErrorSpy).toHaveBeenCalled();
|
||||
consoleErrorSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user