test(feishu): cover native Windows webhook and workspace paths

This commit is contained in:
Vincent Koc
2026-05-04 05:17:11 -07:00
parent 48a3a23d40
commit 4f2f5e0461
2 changed files with 69 additions and 13 deletions

View File

@@ -61,6 +61,8 @@ type ToolResultWithDetails = {
details: Record<string, unknown>;
};
const WORKSPACE_ROOT = path.resolve("/workspace");
describe("feishu_doc image fetch hardening", () => {
beforeEach(() => {
vi.clearAllMocks();
@@ -505,7 +507,7 @@ describe("feishu_doc image fetch hardening", () => {
});
const feishuDocTool = resolveFeishuDocTool({
workspaceDir: "/workspace",
workspaceDir: WORKSPACE_ROOT,
fsPolicy: { workspaceOnly: true },
});
@@ -518,7 +520,7 @@ describe("feishu_doc image fetch hardening", () => {
expect(loadWebMediaMock).toHaveBeenCalledWith(
expect.stringContaining("test-local.txt"),
expect.objectContaining({ optimizeImages: false, localRoots: ["/workspace"] }),
expect.objectContaining({ optimizeImages: false, localRoots: [WORKSPACE_ROOT] }),
);
});
@@ -559,7 +561,7 @@ describe("feishu_doc image fetch hardening", () => {
});
const feishuDocTool = resolveFeishuDocTool({
workspaceDir: "/workspace",
workspaceDir: WORKSPACE_ROOT,
fsPolicy: { workspaceOnly: true },
});
@@ -572,7 +574,7 @@ describe("feishu_doc image fetch hardening", () => {
expect(loadWebMediaMock).toHaveBeenCalledWith(
expect.stringContaining("test-local.png"),
expect.objectContaining({ optimizeImages: false, localRoots: ["/workspace"] }),
expect.objectContaining({ optimizeImages: false, localRoots: [WORKSPACE_ROOT] }),
);
});
@@ -588,7 +590,7 @@ describe("feishu_doc image fetch hardening", () => {
});
const feishuDocTool = resolveFeishuDocTool({
workspaceDir: "/workspace",
workspaceDir: WORKSPACE_ROOT,
fsPolicy: { workspaceOnly: true },
});
@@ -602,7 +604,7 @@ describe("feishu_doc image fetch hardening", () => {
expect(loadWebMediaMock).toHaveBeenCalledWith(
expect.stringContaining("absolute-image.png"),
expect.objectContaining({ optimizeImages: false, localRoots: ["/workspace"] }),
expect.objectContaining({ optimizeImages: false, localRoots: [WORKSPACE_ROOT] }),
);
} finally {
rmSync(fixtureDir, { recursive: true, force: true });

View File

@@ -88,6 +88,64 @@ async function waitForSlowBodyTimeoutResponse(
});
}
async function waitForOversizedBodyResponse(url: string): Promise<string> {
return await new Promise<string>((resolve, reject) => {
const target = new URL(url);
const body = JSON.stringify({ payload: "x".repeat(70 * 1024) });
let response = "";
let settled = false;
const socket = createConnection(
{
host: target.hostname,
port: Number(target.port),
},
() => {
socket.write(`POST ${target.pathname} HTTP/1.1\r\n`);
socket.write(`Host: ${target.hostname}\r\n`);
socket.write("Content-Type: application/json\r\n");
socket.write(`Content-Length: ${Buffer.byteLength(body)}\r\n`);
socket.write("\r\n");
socket.write(body);
},
);
const finish = (result: string) => {
if (settled) {
return;
}
settled = true;
clearTimeout(failTimer);
socket.destroy();
resolve(result);
};
socket.setEncoding("utf8");
socket.on("data", (chunk) => {
response += chunk;
if (response.includes("Payload too large")) {
finish(response);
}
});
socket.on("close", () => {
if (response.includes("Payload too large")) {
finish(response);
}
});
socket.on("error", (error) => {
if (response.includes("Payload too large")) {
finish(response);
return;
}
reject(error);
});
const failTimer = setTimeout(() => {
socket.destroy();
reject(new Error("payload-too-large response did not arrive within 1000ms"));
}, 1_000);
});
}
afterEach(() => {
clearFeishuWebhookRateLimitStateForTest();
stopFeishuMonitor();
@@ -182,14 +240,10 @@ describe("Feishu webhook security hardening", () => {
},
monitorFeishuProvider,
async (url) => {
const response = await fetch(url, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ payload: "x".repeat(70 * 1024) }),
});
const response = await waitForOversizedBodyResponse(url);
expect(response.status).toBe(413);
expect(await response.text()).toBe("Payload too large");
expect(response).toContain("413 Payload Too Large");
expect(response).toContain("Payload too large");
},
);
});