mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
234 lines
8.0 KiB
TypeScript
234 lines
8.0 KiB
TypeScript
import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
|
|
import { chmod, mkdir, open, readdir, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { expect, onTestFinished, test, vi } from "vitest";
|
|
|
|
import * as nativeBound from "../../../src/pcap/PcapNativeBound.js";
|
|
import { PcapProvider } from "../../../src/pcap/PcapProvider.js";
|
|
import {
|
|
PrivateRuntimeRoot,
|
|
type PrivateRuntimeRootOptions,
|
|
} from "../../../src/process/PrivateRuntimeRoot.js";
|
|
import { createServer } from "../../../src/server/createServer.js";
|
|
import { createTestBinarySession } from "../../fixtures/binarySession.js";
|
|
import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
|
|
// POSIX fixtures use real mkdtemp under caller-controlled temporary parents.
|
|
const it = test.skipIf(process.platform === "win32");
|
|
const nonRoot = process.getuid?.() !== 0;
|
|
const stubs = join(import.meta.dirname, "../../fixtures/pcap");
|
|
|
|
type Allocate = (
|
|
options: PrivateRuntimeRootOptions,
|
|
allocate: (options: PrivateRuntimeRootOptions) => Promise<PrivateRuntimeRoot>,
|
|
) => Promise<PrivateRuntimeRoot>;
|
|
|
|
/** Inspect a capture through MCP while only the pcap workspace allocation is redirected. */
|
|
const inspectWith = async (allocate: Allocate, path: string) => {
|
|
const create = PrivateRuntimeRoot.create.bind(PrivateRuntimeRoot);
|
|
vi.spyOn(PrivateRuntimeRoot, "create").mockImplementation((options = {}) =>
|
|
options.prefix === "rea-pcap-"
|
|
? allocate(options, create)
|
|
: create(options),
|
|
);
|
|
// macOS has no native capture bound; admit it so allocation is reached.
|
|
vi.spyOn(nativeBound, "preparePcapNativeBound").mockResolvedValue({
|
|
nativeBound: {},
|
|
limitations: ["Native resource admission simulated by this test"],
|
|
});
|
|
onTestFinished(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
const provider = new PcapProvider({
|
|
environment: {
|
|
REA_TSHARK_COMMAND: join(stubs, "tshark-stub.sh"),
|
|
REA_CAPINFOS_COMMAND: join(stubs, "capinfos-stub.sh"),
|
|
},
|
|
});
|
|
const server = createServer(
|
|
{
|
|
kind: "session",
|
|
session: createTestBinarySession(() => {
|
|
throw new Error("No native binary provider");
|
|
}),
|
|
},
|
|
{ pcapAnalysis: provider },
|
|
);
|
|
const client = new Client({ name: "pcap-workspace-test", version: "1" });
|
|
const [clientTransport, serverTransport] =
|
|
InMemoryTransport.createLinkedPair();
|
|
onTestFinished(async () => {
|
|
await client.close();
|
|
await server.close();
|
|
await provider.close();
|
|
});
|
|
await server.connect(serverTransport);
|
|
await client.connect(clientTransport);
|
|
return parseMcpToolError(
|
|
await client.callTool({
|
|
name: "inspect_pcap_capture",
|
|
arguments: { path },
|
|
}),
|
|
).error;
|
|
};
|
|
|
|
const systemError = (code: string, path: string) =>
|
|
Object.assign(new Error(`${code}: simulated, mkdtemp '${path}'`), {
|
|
code,
|
|
path,
|
|
});
|
|
|
|
it.each<readonly [string, string]>([
|
|
["ENOTDIR", "a regular file"],
|
|
// Root bypasses directory permissions, so EACCES needs a non-root account.
|
|
...(nonRoot ? [["EACCES", "a mode-0500 directory"] as const] : []),
|
|
])(
|
|
"reports real %s allocation under %s as an unusable workspace, not a size limit",
|
|
async (code) => {
|
|
const root = await createTestTempDirectory("rea-pcap-workspace-");
|
|
const parent = join(root, "parent");
|
|
if (code === "ENOTDIR") await writeFile(parent, "not a directory");
|
|
else {
|
|
await mkdir(parent, { mode: 0o500 });
|
|
onTestFinished(() => chmod(parent, 0o700));
|
|
}
|
|
let cause: unknown;
|
|
const error = await inspectWith(
|
|
(options, create) =>
|
|
create({ ...options, parent }).catch((failure: unknown) => {
|
|
cause = failure;
|
|
throw failure;
|
|
}),
|
|
join(root, "missing.pcap"),
|
|
);
|
|
expect(cause).toMatchObject({ code });
|
|
expect(error).toMatchObject({
|
|
code: "execution_failure",
|
|
category: "execution_failure",
|
|
message: expect.stringContaining(
|
|
`Cannot prepare the private analysis workspace (${code})`,
|
|
) as unknown,
|
|
remediation: {
|
|
action: expect.stringContaining("TMPDIR") as unknown,
|
|
},
|
|
details: {
|
|
provider_id: "pcap",
|
|
operation: "inspect_pcap_capture",
|
|
diagnostics: {
|
|
stage: "workspace",
|
|
workspace_path: null,
|
|
path:
|
|
code === "ENOTDIR"
|
|
? `${parent}/.`
|
|
: (expect.stringContaining(join(parent, "rea-pcap-")) as unknown),
|
|
system_code: code,
|
|
},
|
|
},
|
|
});
|
|
expect(JSON.stringify(error)).not.toContain("file-size");
|
|
},
|
|
);
|
|
|
|
it("reaches input admission when the workspace parent is usable", async () => {
|
|
const root = await createTestTempDirectory("rea-pcap-workspace-");
|
|
const error = await inspectWith(
|
|
(options, create) => create({ ...options, parent: root }),
|
|
join(root, "missing.pcap"),
|
|
);
|
|
expect(error.code).toBe("invalid_request");
|
|
expect(
|
|
(await readdir(root)).filter((name) => name.startsWith("rea-pcap-")),
|
|
).toEqual([]);
|
|
});
|
|
|
|
it.each([
|
|
["ENOSPC", "resource_constraint", "Free disk space or quota"],
|
|
["EDQUOT", "resource_constraint", "Free disk space or quota"],
|
|
["EMFILE", "execution_failure", "open-file limit"],
|
|
["ENFILE", "execution_failure", "open-file limit"],
|
|
["EROFS", "execution_failure", "TMPDIR"],
|
|
])(
|
|
"classifies simulated %s allocation by its storage or descriptor cause",
|
|
async (code, expected, recovery) => {
|
|
const root = await createTestTempDirectory("rea-pcap-workspace-");
|
|
const template = join(root, "rea-pcap-XXXXXX");
|
|
const error = await inspectWith(
|
|
() => Promise.reject(systemError(code, template)),
|
|
join(root, "missing.pcap"),
|
|
);
|
|
expect(error).toMatchObject({
|
|
code: expected,
|
|
remediation: { action: expect.stringContaining(recovery) as unknown },
|
|
});
|
|
if (expected === "resource_constraint")
|
|
expect(error.details).toMatchObject({
|
|
resource: "file-size",
|
|
reported_limits: {
|
|
workspace_path: null,
|
|
path: template,
|
|
system_code: code,
|
|
},
|
|
});
|
|
else
|
|
expect(error.details).toMatchObject({
|
|
diagnostics: { stage: "workspace", path: template, system_code: code },
|
|
});
|
|
},
|
|
);
|
|
|
|
test.skipIf(process.platform === "win32" || !nonRoot)(
|
|
"removes a partially prepared workspace whose contents cannot be created",
|
|
async () => {
|
|
const root = await createTestTempDirectory("rea-pcap-workspace-");
|
|
let workspace: string | undefined;
|
|
const error = await inspectWith(
|
|
async (options, create) => {
|
|
const allocated = await create({ ...options, parent: root });
|
|
workspace = allocated.path;
|
|
// The owner-only workspace exists, but its configuration directory cannot.
|
|
await chmod(allocated.path, 0o500);
|
|
return allocated;
|
|
},
|
|
join(root, "missing.pcap"),
|
|
);
|
|
expect(error).toMatchObject({
|
|
code: "execution_failure",
|
|
details: {
|
|
diagnostics: {
|
|
stage: "workspace",
|
|
workspace_path: workspace,
|
|
path: join(workspace ?? "", "configuration"),
|
|
system_code: "EACCES",
|
|
},
|
|
},
|
|
});
|
|
expect(
|
|
(await readdir(root)).filter((name) => name.startsWith("rea-pcap-")),
|
|
).toEqual([]);
|
|
},
|
|
);
|
|
|
|
it("still reports a capture over the snapshot budget as a file-size limit", async () => {
|
|
const root = await createTestTempDirectory("rea-pcap-workspace-");
|
|
const capture = join(root, "oversized.pcap");
|
|
// Sparse: one byte over the 2 GiB owned-snapshot allowance, no data blocks.
|
|
const handle = await open(capture, "w");
|
|
await handle.truncate(2 * 1024 ** 3 + 1);
|
|
await handle.close();
|
|
const error = await inspectWith(
|
|
(options, create) => create({ ...options, parent: root }),
|
|
capture,
|
|
);
|
|
expect(error).toMatchObject({
|
|
code: "resource_constraint",
|
|
details: {
|
|
resource: "file-size",
|
|
reported_limits: { max_capture_bytes: 2 * 1024 ** 3 },
|
|
},
|
|
});
|
|
expect(
|
|
(await readdir(root)).filter((name) => name.startsWith("rea-pcap-")),
|
|
).toEqual([]);
|
|
});
|