Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0992cb3ceb |
@@ -83,8 +83,9 @@ export const writeEvidenceBundle = async (
|
|||||||
if (bytes > policy.maxBytes)
|
if (bytes > policy.maxBytes)
|
||||||
return err(new EvidenceFileError("write", "too-large"));
|
return err(new EvidenceFileError("write", "too-large"));
|
||||||
try {
|
try {
|
||||||
const canonicalParent = await realpath(dirname(resolve(path)));
|
const requestedPath = resolve(path);
|
||||||
const destination = resolve(canonicalParent, basename(resolve(path)));
|
const canonicalParent = await realpath(dirname(requestedPath));
|
||||||
|
const destination = resolve(canonicalParent, basename(requestedPath));
|
||||||
if (!(await isApproved(destination, policy.roots)))
|
if (!(await isApproved(destination, policy.roots)))
|
||||||
return err(new EvidenceFileError("write", "outside-root"));
|
return err(new EvidenceFileError("write", "outside-root"));
|
||||||
const existing = await lstat(destination).catch((cause: unknown) => {
|
const existing = await lstat(destination).catch((cause: unknown) => {
|
||||||
@@ -101,7 +102,7 @@ export const writeEvidenceBundle = async (
|
|||||||
mode: 0o600,
|
mode: 0o600,
|
||||||
fsync: true,
|
fsync: true,
|
||||||
});
|
});
|
||||||
return ok({ path: destination, bytes });
|
return ok({ path: requestedPath, bytes });
|
||||||
} catch (cause: unknown) {
|
} catch (cause: unknown) {
|
||||||
return err(new EvidenceFileError("write", "io", { cause }));
|
return err(new EvidenceFileError("write", "io", { cause }));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { dirname, join } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
|
import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
|
||||||
import type { CallToolResult } from "@modelcontextprotocol/server";
|
import type { CallToolResult } from "@modelcontextprotocol/server";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
@@ -8,12 +9,16 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { BinarySession } from "../src/application/BinarySession.js";
|
import { BinarySession } from "../src/application/BinarySession.js";
|
||||||
import type { AnalysisClient } from "../src/application/AnalysisProvider.js";
|
import type { AnalysisClient } from "../src/application/AnalysisProvider.js";
|
||||||
|
import { probeProcessCaptureCapability } from "../src/application/ProcessHarness.js";
|
||||||
import { observed as ok } from "./fixtures/analysisExecution.js";
|
import { observed as ok } from "./fixtures/analysisExecution.js";
|
||||||
import { createServer } from "../src/server/createServer.js";
|
import { createServer } from "../src/server/createServer.js";
|
||||||
import { evidenceBundleSchema } from "../src/domain/evidenceBundle.js";
|
import { evidenceBundleSchema } from "../src/domain/evidenceBundle.js";
|
||||||
import { silentLogger } from "../src/logger.js";
|
import { silentLogger } from "../src/logger.js";
|
||||||
|
|
||||||
const resources: Array<{ close(): Promise<unknown> }> = [];
|
const resources: Array<{ close(): Promise<unknown> }> = [];
|
||||||
|
const processFixture = fileURLToPath(
|
||||||
|
new URL("./fixtures/processFidelity.mjs", import.meta.url),
|
||||||
|
);
|
||||||
let directory: string | undefined;
|
let directory: string | undefined;
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
await Promise.all(resources.splice(0).map((resource) => resource.close()));
|
await Promise.all(resources.splice(0).map((resource) => resource.close()));
|
||||||
@@ -173,13 +178,14 @@ describe("target-free MCP lifecycle", () => {
|
|||||||
}, 10_000);
|
}, 10_000);
|
||||||
|
|
||||||
it("records approved process residuals in the unknown registry", async () => {
|
it("records approved process residuals in the unknown registry", async () => {
|
||||||
|
if (!(await probeProcessCaptureCapability()).available) return;
|
||||||
const session = new BinarySession(() => client("fixture", []));
|
const session = new BinarySession(() => client("fixture", []));
|
||||||
const server = createServer(session, session, {
|
const server = createServer(session, session, {
|
||||||
logger: silentLogger,
|
logger: silentLogger,
|
||||||
processPolicy: {
|
processPolicy: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
executableRoots: ["/usr/bin"],
|
executableRoots: [dirname(process.execPath)],
|
||||||
workingRoots: ["/tmp"],
|
workingRoots: [dirname(processFixture)],
|
||||||
allowedEnvironment: [],
|
allowedEnvironment: [],
|
||||||
allowExternalNetwork: true,
|
allowExternalNetwork: true,
|
||||||
},
|
},
|
||||||
@@ -196,13 +202,13 @@ describe("target-free MCP lifecycle", () => {
|
|||||||
arguments: {
|
arguments: {
|
||||||
approved: true,
|
approved: true,
|
||||||
unknown_registry_approved: true,
|
unknown_registry_approved: true,
|
||||||
executable: "/usr/bin/printf",
|
executable: process.execPath,
|
||||||
arguments: ["abcdef"],
|
arguments: [processFixture, "partial"],
|
||||||
working_directory: "/tmp",
|
working_directory: dirname(processFixture),
|
||||||
limits: { output_bytes: 1 },
|
limits: { output_bytes: 1 },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(captured.isError).not.toBe(true);
|
expect(captured.isError, text(captured)).not.toBe(true);
|
||||||
const listed = z
|
const listed = z
|
||||||
.object({
|
.object({
|
||||||
result: z.array(z.object({ question: z.string(), domain: z.string() })),
|
result: z.array(z.object({ question: z.string(), domain: z.string() })),
|
||||||
|
|||||||
Reference in New Issue
Block a user