From 0992cb3cebc429ff55d6e7b2726d93c45a763a2a Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Mon, 13 Jul 2026 15:20:21 +0800 Subject: [PATCH] fix: preserve requested evidence paths on macOS --- src/application/EvidenceBundleFiles.ts | 7 ++++--- tests/sessionMcp.test.ts | 20 +++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/application/EvidenceBundleFiles.ts b/src/application/EvidenceBundleFiles.ts index be2cd2915..f6e50449d 100644 --- a/src/application/EvidenceBundleFiles.ts +++ b/src/application/EvidenceBundleFiles.ts @@ -83,8 +83,9 @@ export const writeEvidenceBundle = async ( if (bytes > policy.maxBytes) return err(new EvidenceFileError("write", "too-large")); try { - const canonicalParent = await realpath(dirname(resolve(path))); - const destination = resolve(canonicalParent, basename(resolve(path))); + const requestedPath = resolve(path); + const canonicalParent = await realpath(dirname(requestedPath)); + const destination = resolve(canonicalParent, basename(requestedPath)); if (!(await isApproved(destination, policy.roots))) return err(new EvidenceFileError("write", "outside-root")); const existing = await lstat(destination).catch((cause: unknown) => { @@ -101,7 +102,7 @@ export const writeEvidenceBundle = async ( mode: 0o600, fsync: true, }); - return ok({ path: destination, bytes }); + return ok({ path: requestedPath, bytes }); } catch (cause: unknown) { return err(new EvidenceFileError("write", "io", { cause })); } diff --git a/tests/sessionMcp.test.ts b/tests/sessionMcp.test.ts index 7ef75c713..21144d061 100644 --- a/tests/sessionMcp.test.ts +++ b/tests/sessionMcp.test.ts @@ -1,6 +1,7 @@ import { mkdtemp, rm, writeFile } from "node:fs/promises"; 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 type { CallToolResult } from "@modelcontextprotocol/server"; import { afterEach, describe, expect, it } from "vitest"; @@ -8,12 +9,16 @@ import { z } from "zod"; import { BinarySession } from "../src/application/BinarySession.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 { createServer } from "../src/server/createServer.js"; import { evidenceBundleSchema } from "../src/domain/evidenceBundle.js"; import { silentLogger } from "../src/logger.js"; const resources: Array<{ close(): Promise }> = []; +const processFixture = fileURLToPath( + new URL("./fixtures/processFidelity.mjs", import.meta.url), +); let directory: string | undefined; afterEach(async () => { await Promise.all(resources.splice(0).map((resource) => resource.close())); @@ -173,13 +178,14 @@ describe("target-free MCP lifecycle", () => { }, 10_000); it("records approved process residuals in the unknown registry", async () => { + if (!(await probeProcessCaptureCapability()).available) return; const session = new BinarySession(() => client("fixture", [])); const server = createServer(session, session, { logger: silentLogger, processPolicy: { enabled: true, - executableRoots: ["/usr/bin"], - workingRoots: ["/tmp"], + executableRoots: [dirname(process.execPath)], + workingRoots: [dirname(processFixture)], allowedEnvironment: [], allowExternalNetwork: true, }, @@ -196,13 +202,13 @@ describe("target-free MCP lifecycle", () => { arguments: { approved: true, unknown_registry_approved: true, - executable: "/usr/bin/printf", - arguments: ["abcdef"], - working_directory: "/tmp", + executable: process.execPath, + arguments: [processFixture, "partial"], + working_directory: dirname(processFixture), limits: { output_bytes: 1 }, }, }); - expect(captured.isError).not.toBe(true); + expect(captured.isError, text(captured)).not.toBe(true); const listed = z .object({ result: z.array(z.object({ question: z.string(), domain: z.string() })),