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: "![x](https://x.test/image.png)", }); expect(fetchRemoteMediaMock).toHaveBeenCalled(); expect(driveUploadAllMock).not.toHaveBeenCalled(); expect(blockPatchMock).not.toHaveBeenCalled(); expect(result.details.images_processed).toBe(0); expect(consoleErrorSpy).toHaveBeenCalled(); consoleErrorSpy.mockRestore(); }); });