import { OwnedCommandFailure } from "../../../../src/process/OwnedCommand.js"; import { createHash } from "node:crypto"; import { DarwinProcessOwnershipInspectionError } from "../../../../src/process/DarwinProcessRunTokenReader.js"; import { EventEmitter } from "node:events"; import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { PassThrough } from "node:stream"; import { afterEach, beforeAll, describe, expect, it } from "vitest"; import { createTestTempDirectory } from "../../../fixtures/temporaryDirectory.js"; import { NativeMacOSProvider } from "../../../../src/native/NativeMacOSProvider.js"; import { NATIVE_COMMAND_OUTPUT_BUDGET_BYTES, NATIVE_FAILURE_DIAGNOSTIC_BYTES, NativeCommandFailure, XcrunCommandRunner, type NativeCommandLauncher, type NativeCommandRunner, } from "../../../../src/native/CommandRunner.js"; import { err, ok } from "../../../../src/domain/result.js"; import { parseLipoOutput } from "../../../../src/native/parsers/lipo.js"; import { parseCodeSignature } from "../../../../src/native/parsers/codesign.js"; import { prepareProcessOwnershipInspection } from "../../../../src/process/ProcessOwnershipObservation.js"; import { projectAnalysisError } from "../../../../src/domain/analysisErrorProjection.js"; import { ProviderCleanupError } from "../../../../src/domain/providerCleanupError.js"; import { cleanupOwnedProcessGroup } from "../../../../src/process/ProcessOwnership.js"; import { ProviderProcessSupervisor, spawnOwnedProviderProcess, type ProviderProcessHandle, type SpawnedOwnedProviderProcess, } from "../../../../src/process/ProviderProcess.js"; import { NativeFixtureRunner as FixtureRunner, nativeFixture as fixture, nativeMachoTarget as machoTarget, nativeMachoTargetForFile, } from "../../../fixtures/nativeCommands.js"; const cleanupOwner = (runId: string): ProviderProcessSupervisor => { const child = Object.assign(new EventEmitter(), { stdout: new PassThrough(), stderr: new PassThrough(), exitCode: null as number | null, signalCode: null as NodeJS.Signals | null, kill: () => true, }); return new ProviderProcessSupervisor({ process: child, ownsProcessLifetime: true, ownership: { runId, leaderPid: 46_000, processGroupId: 46_000, expectedParentPid: process.pid, }, cleanup: async () => ({ cleaned: false, reason: "still running" }), }); }; beforeAll(() => prepareProcessOwnershipInspection()); let directory: string | undefined; afterEach(async () => { if (directory !== undefined) await rm(directory, { recursive: true, force: true }); directory = undefined; }); describe("native macOS provider discovery", () => { it.each([ "ordinary.fixture", " spaced.fixture ", " leading", "trailing ", "literal\r\nidentifier\r", ])( "preserves the exact signing identifier %j reported by codesign", (identifier) => { const parsed = parseCodeSignature( `Identifier=${identifier}\nFormat=Mach-O thin (arm64)\nTeamIdentifier=not set\n`, false, ); expect(parsed.identifier).toBe(identifier); expect(parsed.team_identifier).toBeNull(); expect(parsed.format).toBe("Mach-O thin (arm64)"); }, ); it.each(["\n", "\r\n"])( "separates echoed path and identifier text from signature fields (%j)", (newline) => { const path = "/apps/x\r\nAuthority=Forged\nIdentifier=forged"; const identifier = "x\r\nFormat=forged\nAuthority=Forged-5555\r"; const parsed = parseCodeSignature( [ `Executable=${path}`, `Identifier=${identifier}`, "Format=Mach-O thin (arm64)", "CodeDirectory v=20400 size=342 flags=0x2(adhoc) hashes=2+2", "CDHash=6aee90e6", "Signature=adhoc", "TeamIdentifier=not set", "", ].join(newline), false, ["/elsewhere", path], ); expect(parsed).toMatchObject({ identifier, format: "Mach-O thin (arm64)", authorities: [], cdhashes: ["6aee90e6"], team_identifier: null, }); }, ); it.each([ { executable: "", paths: [], newline: "\r\n", identifier: "fixture" }, { executable: "Executable=/apps/fixture\r\n", paths: [], newline: "\r\n", identifier: "fixture\nFormat=payload\r", }, { executable: "Executable=/apps/fixture\n", paths: ["/elsewhere"], newline: "\n", identifier: "fixture\r\nFormat=payload\r", }, { executable: "Executable=/apps/fixture\r\n", paths: ["/elsewhere"], newline: "\r\n", identifier: "fixture\r", }, ])( "frames the identifier without a matching executable path: %j", ({ executable, paths, newline, identifier }) => { const parsed = parseCodeSignature( `${executable}Identifier=${identifier}${newline}Format=Mach-O thin (arm64)${newline}TeamIdentifier=not set${newline}`, false, paths, ); expect(parsed.identifier).toBe(identifier); expect(parsed.format).toBe("Mach-O thin (arm64)"); expect(parsed.team_identifier).toBeNull(); }, ); it.each(["timeout", "output-limit"] as const)( "preserves %s resolution failures with their captured output and cleanup status", async (reason) => { const failure = new NativeCommandFailure( "file", reason, 9, undefined, { stdout: "partial xcrun output", stderr: "partial xcrun diagnostic", stdoutBytes: 20, stderrBytes: 25, exitCode: 9, signal: null, truncated: reason === "output-limit", }, "ownership cleanup was not confirmed", ["process-group:1234"], ); const runner = new XcrunCommandRunner({}, () => Promise.resolve(err(failure)), ); const result = await runner.run("file", [], {}); expect(result).toEqual(err(failure)); if (result.ok) throw new Error("Resolution failure unexpectedly passed"); expect(result.error).toMatchObject({ reason, capture: { stdout: "partial xcrun output", truncated: reason === "output-limit", }, cleanupFailure: "ownership cleanup was not confirmed", cleanupResources: ["process-group:1234"], }); }, ); }); describe("native command output collection", () => { it("retries discovery and resamples the executable after its tool path is replaced", async () => { directory = await createTestTempDirectory("rea-native-tool-identity-"); const executable = join(directory, "fixture-tool"); const source = (value: string): string => `#!/bin/sh\nprintf '%s\\n' '${value}'\n`; const install = async (value: string): Promise => { const contents = source(value); await writeFile(executable, contents, { mode: 0o700 }); await chmod(executable, 0o700); return createHash("sha256").update(contents).digest("hex"); }; let unavailable = true; const runner = new XcrunCommandRunner({}, async (tool) => { if (unavailable) { unavailable = false; return err(new NativeCommandFailure(tool, "unavailable")); } const digest = createHash("sha256") .update(await readFile(executable)) .digest("hex"); return ok({ path: executable, sha256: digest }); }); const originalDigest = await install("original-tool-output"); expect(await runner.run("file", [], {})).toMatchObject({ ok: false, error: { reason: "unavailable" }, }); const original = await runner.run("file", [], {}); if (!original.ok) throw original.error; expect(original.value.stdout).toBe("original-tool-output\n"); expect(original.value.executableSha256).toBe(originalDigest); const replacementDigest = await install("replacement-tool-output"); const replacement = await runner.run("file", [], {}); if (!replacement.ok) throw replacement.error; expect(replacement.value.stdout).toBe("replacement-tool-output\n"); expect(replacement.value.executableSha256).toBe(replacementDigest); expect(replacement.value.versionReason).toContain("prelaunch file sample"); expect(replacement.value.versionReason).toContain( "does not prove atomic OS image binding", ); }); it("retains the complete native command output", async () => { const runner = new XcrunCommandRunner({}, () => Promise.resolve(ok({ path: process.execPath, sha256: "a".repeat(64) })), ); const output = "x".repeat(4096); const captured = await runner.run( "file", ["-e", `process.stdout.write(${JSON.stringify(output)})`], {}, ); expect(captured.ok && captured.value.stdout).toBe(output); }); it("retains both output streams and exit status when a command fails", async () => { const runner = new XcrunCommandRunner({}, () => Promise.resolve(ok({ path: process.execPath, sha256: "a".repeat(64) })), ); const result = await runner.run( "file", [ "-e", 'process.stdout.write("partial stdout"); process.stderr.write("partial stderr"); process.exitCode = 7', ], {}, ); expect(result.ok).toBe(false); if (result.ok) return; expect(result.error).toMatchObject({ reason: "nonzero-exit", exitCode: 7, capture: { stdout: "partial stdout", stderr: "partial stderr", stdoutBytes: 14, stderrBytes: 14, exitCode: 7, signal: null, truncated: false, }, }); }); it("retains accepted nonzero output as a complete command capture", async () => { const runner = new XcrunCommandRunner({}, () => Promise.resolve(ok({ path: process.execPath, sha256: "a".repeat(64) })), ); const result = await runner.run( "file", [ "-e", 'process.stdout.write("unsigned observation"); process.exitCode = 1', ], { acceptNonZero: true }, ); expect(result).toMatchObject({ ok: true, value: { stdout: "unsigned observation", stdoutBytes: 20, exitCode: 1, signal: null, }, }); }); }); describe("native command failure capture and limits", () => { it("projects captured diagnostics from a failed native provider command", async () => { const runner = new XcrunCommandRunner({}, () => Promise.resolve(ok({ path: process.execPath, sha256: "a".repeat(64) })), ); const result = await new NativeMacOSProvider({}, runner, "darwin") .createClient(machoTarget("/private/fixture")) .execute("demangle_swift", { symbols: ["fixture-symbol"] }); expect(result.ok).toBe(false); if (result.ok) return; expect(result.error._tag).toBe("ProviderAdapterError"); expect(result.error.capturedOutput?.stderr).toContain("Cannot find module"); expect(result.error.capturedOutput?.stderr_bytes).toBeGreaterThan(0); expect(result.error.capturedOutput?.exit_code).not.toBe(0); expect(projectAnalysisError(result.error).details).toMatchObject({ captured_output: { stderr: expect.stringContaining("Cannot find module"), stderr_bytes: expect.any(Number), exit_code: expect.any(Number), }, diagnostics: { reason: "nonzero-exit", stderr_bytes: expect.any(Number), }, }); expect(projectAnalysisError(result.error).details).not.toHaveProperty( "diagnostics.detail", ); }); it("projects the ownership helper prerequisite when a command cannot be supervised", async () => { const inspection = new DarwinProcessOwnershipInspectionError( "Direct Darwin helper compilation requires absolute REA_DARWIN_SWIFTC_PATH and SDKROOT paths.", ); const runner = new XcrunCommandRunner( {}, () => Promise.resolve(ok({ path: process.execPath, sha256: "a".repeat(64) })), () => Promise.reject(inspection), ); const result = await new NativeMacOSProvider({}, runner, "darwin") .createClient(machoTarget("/private/fixture")) .execute("demangle_swift", { symbols: ["fixture-symbol"] }); expect(result.ok).toBe(false); if (result.ok) return; expect(projectAnalysisError(result.error).details).toMatchObject({ diagnostics: { phase: "process-preparation", reason: "io", detail: inspection.message, }, }); expect(result.error.capturedOutput).toBeUndefined(); }); it("retains output written before cancellation and reaps the stalled command", async () => { const runner = new XcrunCommandRunner({}, () => Promise.resolve(ok({ path: process.execPath, sha256: "a".repeat(64) })), ); const controller = new AbortController(); const pending = runner.run( "file", [ "-e", 'process.stdout.write("started"); process.stderr.write("warning"); setInterval(() => {}, 1000)', ], { signal: controller.signal }, ); setTimeout(() => controller.abort(), 100); const result = await pending; expect(result.ok).toBe(false); if (result.ok) return; expect(result.error).toMatchObject({ reason: "cancelled", capture: { stdout: "started", stderr: "warning", stdoutBytes: 7, stderrBytes: 7, truncated: false, }, }); }); }); describe("native owned cancellation boundary", () => { it.skipIf(process.platform === "win32")( "projects an owned Node command cancellation with its exact reason and captured output", async () => { directory = await createTestTempDirectory("rea-native-cancel-boundary-"); const ready = join(directory, "ready"); const nodeSource = [ 'process.stdout.write("native output before cancellation", () => {', 'process.stderr.write("native diagnostic before cancellation", () => {', `require("node:fs").writeFileSync(${JSON.stringify(ready)}, "ready");`, "}); });", "setInterval(() => {}, 1000);", ].join("\n"); const executable = join(directory, "native-tool"); await writeFile(executable, `#!${process.execPath}\n${nodeSource}\n`, { mode: 0o700, }); await chmod(executable, 0o700); const executableSha256 = createHash("sha256") .update(await readFile(executable)) .digest("hex"); const launcher: NativeCommandLauncher = (options) => spawnOwnedProviderProcess({ ...options, expectedCommand: null }); const runner = new XcrunCommandRunner( {}, async () => ok({ path: executable, sha256: executableSha256 }), launcher, ); const client = new NativeMacOSProvider({}, runner, "darwin").createClient( machoTarget("/private/fixture"), ); const controller = new AbortController(); const cancellationReason = { source: "test caller", operation: "native command cancellation", }; try { const pending = client.execute( "list_architectures", {}, { signal: controller.signal }, ); const deadline = Date.now() + 5_000; let readyObserved = false; while (Date.now() < deadline) { try { readyObserved = (await readFile(ready, "utf8")) === "ready"; } catch (cause: unknown) { if ( !( cause instanceof Error && "code" in cause && cause.code === "ENOENT" ) ) throw cause; } if (readyObserved) break; await new Promise((resolve) => setTimeout(resolve, 10)); } expect( readyObserved, "the owned Node process reached its output barrier", ).toBe(true); controller.abort(cancellationReason); const result = await pending; if (result.ok) throw new Error("Expected owned command cancellation"); expect(result.error._tag).toBe("AnalysisCancelledError"); expect(result.error.cause).toBe(cancellationReason); expect(result.error.capturedOutput).toMatchObject({ stdout: "native output before cancellation", stderr: "native diagnostic before cancellation", truncated: false, }); expect(result.error.cleanupIncomplete).toBe(false); await expect(client.close()).resolves.toMatchObject({ ok: true }); } finally { await client.close(); } }, ); }); describe("native cancellation cleanup provenance", () => { it.skipIf(process.platform === "win32")( "resolves only the owners that succeed when cleanup retries cover multiple native commands", async () => { directory = await createTestTempDirectory("rea-native-mixed-cleanup-"); const executable = join(directory, "native-tool"); const nodeSource = 'require("node:fs").writeFileSync(require("node:path").join(' + JSON.stringify(directory) + ', process.env.REA_PROCESS_RUN_ID), "ready"); setInterval(() => {}, 1000);\n'; await writeFile(executable, "#!" + process.execPath + "\n" + nodeSource, { mode: 0o700, }); await chmod(executable, 0o700); const executableSha256 = createHash("sha256") .update(await readFile(executable)) .digest("hex"); const owners: Array<{ readonly runId: string; attempts: number; cleanupVerified: boolean; process: SpawnedOwnedProviderProcess["process"] | undefined; }> = []; const launcher: NativeCommandLauncher = async (options) => { const owner: (typeof owners)[number] = { runId: options.runId, attempts: 0, cleanupVerified: false, process: undefined, }; owners.push(owner); const launched = await spawnOwnedProviderProcess({ ...options, expectedCommand: null, }); owner.process = launched.process; return { ...launched, cleanup: async () => { owner.attempts += 1; if ( owner.attempts === 1 || (owner === owners[1] && owner.attempts === 2) ) return { cleaned: false, reason: `controlled owner ${owner.runId} refusal ${owner.attempts}`, }; const cleanup = await cleanupOwnedProcessGroup(launched.ownership); owner.cleanupVerified = cleanup.cleaned; return cleanup; }, }; }; const runner = new XcrunCommandRunner( {}, async () => ok({ path: executable, sha256: executableSha256 }), launcher, ); const client = new NativeMacOSProvider({}, runner, "darwin").createClient( machoTarget("/private/fixture"), ); const controllers = [new AbortController(), new AbortController()]; const cancellationReason = { source: "mixed native owner retry" }; try { const pending = controllers.map((controller) => client.execute( "list_architectures", {}, { signal: controller.signal }, ), ); const deadline = Date.now() + 5_000; let ready = false; while (Date.now() < deadline && !ready) { if (owners.length === 2) { const markers = await Promise.all( owners.map(async ({ runId }) => { try { return ( (await readFile(join(directory!, runId), "utf8")) === "ready" ); } catch (cause: unknown) { if ( !( cause instanceof Error && "code" in cause && cause.code === "ENOENT" ) ) throw cause; return false; } }), ); ready = markers.every(Boolean); } if (!ready) await new Promise((resolve) => setTimeout(resolve, 10)); } expect( ready, "both owned processes reached their filesystem barriers", ).toBe(true); for (const controller of controllers) controller.abort(cancellationReason); const results = await Promise.all(pending); const failures = results.map((result) => { if (result.ok) throw new Error("Expected command cancellation"); expect(result.error.cleanupIncomplete).toBe(true); return result.error; }); const failuresByOwner = owners.map((owner) => { const failure = failures.find((error) => error.cleanupResources.includes(owner.runId), ); if (failure === undefined) throw new Error("Missing error for retained native owner"); return failure; }); await runner.retryCleanup?.(); expect(failuresByOwner[0]?.cleanupIncomplete).toBe(false); expect(failuresByOwner[0]?.cleanupResources).toEqual([]); expect(owners[0]?.cleanupVerified).toBe(true); expect( owners[0]?.process !== undefined && (owners[0].process.exitCode !== null || owners[0].process.signalCode !== null), ).toBe(true); expect(failuresByOwner[1]?.cleanupIncomplete).toBe(true); expect(failuresByOwner[1]?.cleanupResources.length).toBeGreaterThan(0); await runner.retryCleanup?.(); expect(failuresByOwner[1]?.cleanupIncomplete).toBe(false); expect(failuresByOwner[1]?.cleanupResources).toEqual([]); expect(owners[1]?.cleanupVerified).toBe(true); expect( owners[1]?.process !== undefined && (owners[1].process.exitCode !== null || owners[1].process.signalCode !== null), ).toBe(true); } finally { for (const { process: child } of owners) if (child?.exitCode === null) child.kill("SIGKILL"); await client.close(); } }, ); }); describe("native returned cleanup ownership", () => { it.skipIf(process.platform === "win32")( "resolves a returned command error when its exact retained owner later stops", async () => { directory = await createTestTempDirectory("rea-native-cleanup-retry-"); const ready = join(directory, "ready"); const executable = join(directory, "native-tool"); await writeFile( executable, `#!${process.execPath}\nrequire("node:fs").writeFileSync(${JSON.stringify(ready)}, "ready"); setInterval(() => {}, 1000);\n`, { mode: 0o700 }, ); await chmod(executable, 0o700); const executableSha256 = createHash("sha256") .update(await readFile(executable)) .digest("hex"); let cleanupCalls = 0; let verifiedCleanup = false; let ownedProcess: ProviderProcessHandle | undefined; const launcher: NativeCommandLauncher = async (options) => { const launched = await spawnOwnedProviderProcess({ ...options, expectedCommand: null, }); ownedProcess = launched.process; return { ...launched, cleanup: async () => { cleanupCalls += 1; if (cleanupCalls < 3) return { cleaned: false, reason: "controlled first cleanup refusal", }; const cleanup = await cleanupOwnedProcessGroup(launched.ownership); verifiedCleanup = cleanup.cleaned; return cleanup; }, }; }; const runner = new XcrunCommandRunner( {}, async () => ok({ path: executable, sha256: executableSha256 }), launcher, ); const client = new NativeMacOSProvider({}, runner, "darwin").createClient( machoTarget("/private/fixture"), ); const controller = new AbortController(); const cancellationReason = { source: "native cleanup retry regression" }; try { const pending = client.execute( "list_architectures", {}, { signal: controller.signal }, ); const deadline = Date.now() + 5_000; let readyObserved = false; while (Date.now() < deadline) { try { readyObserved = (await readFile(ready, "utf8")) === "ready"; } catch (cause: unknown) { if ( !( cause instanceof Error && "code" in cause && cause.code === "ENOENT" ) ) throw cause; } if (readyObserved) break; await new Promise((resolve) => setTimeout(resolve, 10)); } expect( readyObserved, "owned Node process reached filesystem barrier", ).toBe(true); controller.abort(cancellationReason); const original = await pending; if (original.ok) throw new Error("Expected command cancellation"); expect(original.error.cause).toBe(cancellationReason); expect(original.error.cleanupIncomplete).toBe(true); expect( original.error.cleanupResources.some((resource) => resource.startsWith("process-group:"), ), ).toBe(true); expect(cleanupCalls).toBe(1); // Client close retries the same retained owner and returns a live // aggregate marker when that retry is still incomplete. const closed = await client.close(); expect(cleanupCalls).toBe(2); expect(closed.ok).toBe(false); if (closed.ok) throw new Error("Expected cleanup to remain incomplete"); expect(closed.error.cleanupIncomplete).toBe(true); expect(closed.error.cleanupResources).toEqual( original.error.cleanupResources, ); await runner.retryCleanup?.(); expect(cleanupCalls).toBe(3); expect(verifiedCleanup).toBe(true); expect( ownedProcess !== undefined && (ownedProcess.exitCode !== null || ownedProcess.signalCode !== null), ).toBe(true); expect(original.error.cleanupIncomplete).toBe(false); expect(original.error.cleanupResources).toEqual([]); expect(closed.error.cleanupIncomplete).toBe(false); expect(closed.error.cleanupResources).toEqual([]); await expect(client.close()).resolves.toMatchObject({ ok: true }); } finally { if (ownedProcess !== undefined && ownedProcess.exitCode === null) ownedProcess.kill("SIGKILL"); await client.close(); } }, ); }); describe("native late cancellation with cleanup", () => { it("keeps a late cancellation fact with the physical command failure and retries its retained owner", async () => { const cancellationReason = { caller: "stop after process failure" }; const controller = new AbortController(); let commandStarted!: () => void; const started = new Promise((resolve) => { commandStarted = resolve; }); let cleanupStarted!: () => void; const cleanupAttempt = new Promise((resolve) => { cleanupStarted = resolve; }); let releaseFirstCleanup!: () => void; const firstCleanup = new Promise((resolve) => { releaseFirstCleanup = resolve; }); let cleanupCalls = 0; let launches = 0; const launcher: NativeCommandLauncher = async (options) => { launches += 1; const child = Object.assign(new EventEmitter(), { stdout: new PassThrough(), stderr: new PassThrough(), exitCode: null as number | null, signalCode: null as NodeJS.Signals | null, kill: () => true, }); setImmediate(() => { child.stdout.write("partial native output"); child.stderr.write("native diagnostic"); child.stdout.end(); child.stderr.end(); child.exitCode = 7; child.emit("exit", 7, null); child.emit("close", 7, null); commandStarted(); }); return { process: child, ownership: { runId: options.runId, leaderPid: 47_100 + launches, processGroupId: 47_100 + launches, expectedParentPid: process.pid, }, cleanup: async () => { cleanupCalls += 1; if (cleanupCalls === 1) { cleanupStarted(); await firstCleanup; return { cleaned: false, reason: "controlled cleanup remains incomplete", }; } return { cleaned: true, signaled: false }; }, }; }; const runner = new XcrunCommandRunner( {}, async () => ok({ path: "/controlled/native-tool", sha256: "a".repeat(64) }), launcher, ); const client = new NativeMacOSProvider({}, runner, "darwin").createClient( machoTarget("/private/fixture"), ); try { const pending = client.execute( "list_architectures", {}, { signal: controller.signal }, ); await started; await cleanupAttempt; controller.abort(cancellationReason); releaseFirstCleanup(); const result = await pending; if (result.ok) throw new Error("Expected the process failure to remain primary"); expect(result.error._tag).toBe("ProviderAdapterError"); expect(result.error.cause).toMatchObject({ reason: "nonzero-exit", capture: { stdout: "partial native output", stderr: "native diagnostic", exitCode: 7, }, cleanupFailure: "controlled cleanup remains incomplete", }); expect(result.error.cause).toMatchObject({ cancellation: { reason: cancellationReason }, }); expect(result.error.cause).toHaveProperty("cleanupOwner"); expect(result.error.cleanup).toMatchObject({ reason: "controlled cleanup remains incomplete", }); expect(cleanupCalls).toBe(1); expect(launches).toBe(1); await expect(client.close()).resolves.toMatchObject({ ok: true }); expect(cleanupCalls).toBe(2); expect(launches).toBe(1); } finally { releaseFirstCleanup(); await client.close(); } }); }); describe("native command failure capture and limits", () => { it("bounds both failure streams at a UTF-8 boundary while keeping their totals", async () => { const runner = new XcrunCommandRunner({}, () => Promise.resolve(ok({ path: process.execPath, sha256: "a".repeat(64) })), ); // "é" is two bytes and straddles the excerpt boundary, so it is dropped whole. const stdout = `${"x".repeat(NATIVE_FAILURE_DIAGNOSTIC_BYTES - 1)}é${"y".repeat(1024)}`; const stderr = "z".repeat(NATIVE_FAILURE_DIAGNOSTIC_BYTES + 1024); // Build the output in the child: one Linux argument is limited to 128 KiB. const result = await runner.run( "file", [ "-e", `const stdout = "x".repeat(${String(NATIVE_FAILURE_DIAGNOSTIC_BYTES - 1)}) + "\u00e9" + "y".repeat(1024); const stderr = "z".repeat(${String(NATIVE_FAILURE_DIAGNOSTIC_BYTES + 1024)}); process.stdout.write(stdout, () => process.stderr.write(stderr, () => process.exit(3)));`, ], {}, ); expect(result.ok).toBe(false); if (result.ok) return; expect(result.error.reason).toBe("nonzero-exit"); expect(result.error.capture).toMatchObject({ stdout: "x".repeat(NATIVE_FAILURE_DIAGNOSTIC_BYTES - 1), stderr: "z".repeat(NATIVE_FAILURE_DIAGNOSTIC_BYTES), stdoutBytes: Buffer.byteLength(stdout), stderrBytes: Buffer.byteLength(stderr), exitCode: 3, truncated: true, }); }); it("rejects output beyond the complete native-command budget", async () => { const runner = new XcrunCommandRunner({}, () => Promise.resolve(ok({ path: process.execPath, sha256: "a".repeat(64) })), ); const megabytesOverBudget = Math.ceil(NATIVE_COMMAND_OUTPUT_BUDGET_BYTES / (1024 * 1024)) + 1; const result = await runner.run( "file", [ "-e", `async function main() { const chunk = "x".repeat(1024 * 1024); for (let i = 0; i < ${String(megabytesOverBudget)}; i += 1) await new Promise((resolve) => process.stdout.write(chunk, resolve)); } void main()`, ], {}, ); expect(result.ok).toBe(false); if (result.ok) return; expect(result.error.reason).toBe("output-limit"); expect(result.error.capture?.truncated).toBe(true); expect( (result.error.capture?.stdoutBytes ?? 0) + (result.error.capture?.stderrBytes ?? 0), ).toBeLessThanOrEqual(NATIVE_COMMAND_OUTPUT_BUDGET_BYTES); // The failure carries an excerpt, not the whole retained output budget. expect(result.error.capture?.stdout.length).toBe( NATIVE_FAILURE_DIAGNOSTIC_BYTES, ); expect(result.error.capture?.stdoutBytes).toBeGreaterThan( NATIVE_FAILURE_DIAGNOSTIC_BYTES, ); }); }); describe("native command cleanup lifecycle", () => { it("keeps a not-owned retry unresolved instead of discarding its marker", async () => { const child = Object.assign(new EventEmitter(), { stdout: new PassThrough(), stderr: new PassThrough(), exitCode: null as number | null, signalCode: null as NodeJS.Signals | null, kill: () => true, }); const owner = new ProviderProcessSupervisor({ process: child, ownsProcessLifetime: false, }); let resolveCalls = 0; const runner = new XcrunCommandRunner({}, async (tool) => { resolveCalls += 1; return err( new NativeCommandFailure( tool, "io", null, { cleanupOwner: owner }, null, "initial cleanup remains unresolved", ["run:unowned"], ), ); }); const initial = await runner.run("file", [], {}); if (initial.ok) throw new Error("Expected resolver failure"); expect(initial.error.cleanupMarker?.cleanupIncomplete).toBe(true); const blocked = await runner.run("file", [], {}); if (blocked.ok) throw new Error("Expected unresolved cleanup to block launch"); expect(blocked.error.cleanupFailures).toHaveLength(1); expect(blocked.error.cleanupFailures[0]?.reason).toBe( "native command ownership remains unresolved", ); expect(await runner.retryCleanup()).toHaveLength(1); expect(initial.error.cleanupMarker?.cleanupIncomplete).toBe(true); expect(resolveCalls).toBe(1); }); it("waits for an active command before retrying its failed owner at close", async () => { let finishCommand!: ( result: Awaited>, ) => void; let commandStarted!: () => void; const started = new Promise((resolve) => { commandStarted = resolve; }); const command = new Promise< Awaited> >((resolve) => { finishCommand = resolve; }); let cleanupRetries = 0; const runner: NativeCommandRunner = { run: () => { commandStarted(); return command; }, retryCleanup: async () => { cleanupRetries += 1; return cleanupRetries === 1 ? [ { owner: cleanupOwner("run:first"), reason: "first process group remains live", resources: ["run:first", "process-group:1234"], }, { owner: cleanupOwner("run:second"), reason: "second process group remains live", resources: ["run:second", "process-group:5678"], }, ] : []; }, }; const client = new NativeMacOSProvider({}, runner, "darwin").createClient( machoTarget("/private/fixture"), ); const execution = client.execute("list_architectures", {}); await started; const closing = client.close(); await Promise.resolve(); expect(cleanupRetries).toBe(0); finishCommand( err( new NativeCommandFailure("lipo", "timeout", null, undefined, { stdout: "partial architecture output", stderr: "partial diagnostic", stdoutBytes: 27, stderrBytes: 19, exitCode: null, signal: null, truncated: false, }), ), ); const [observed, closed] = await Promise.all([execution, closing]); expect(observed.ok).toBe(false); expect(closed.ok).toBe(false); expect(cleanupRetries).toBe(1); if (!closed.ok) { expect(closed.error.cleanupResources).toEqual([ "run:first", "process-group:1234", "run:second", "process-group:5678", ]); expect(closed.error).toHaveProperty("diagnostics", { reason: "Native command process cleanup remains incomplete", failures: [ { reason: "first process group remains live", resources: ["run:first", "process-group:1234"], }, { reason: "second process group remains live", resources: ["run:second", "process-group:5678"], }, ], }); } await expect(client.close()).resolves.toMatchObject({ ok: true }); expect(cleanupRetries).toBe(2); }); }); describe("native command failure projection", () => { it("does not clear mixed cleanup facts when one injected owner has no marker", async () => { const marker = new ProviderCleanupError("native-macos", ["run:known"], { reason: "known owner remains incomplete", }); const failures = [ { owner: cleanupOwner("run:known"), reason: "known owner remains incomplete", resources: ["run:known"], marker, }, { owner: cleanupOwner("run:unknown"), reason: "unknown owner remains incomplete", resources: ["run:unknown"], }, ]; const runner: NativeCommandRunner = { run: async (tool) => err( new NativeCommandFailure( tool, "io", null, { cleanupFailures: failures }, null, "mixed cleanup remains incomplete", ["run:known", "run:unknown"], ), ), retryCleanup: async () => failures, }; const client = new NativeMacOSProvider({}, runner, "darwin").createClient( machoTarget("/private/fixture"), ); try { const result = await client.execute("list_architectures", {}); if (result.ok) throw new Error("Expected native command failure"); expect(result.error.cleanupIncomplete).toBe(true); marker.resolveCleanup(); expect(result.error.cleanupIncomplete).toBe(true); expect(result.error.cleanupResources).toEqual([ "run:known", "run:unknown", ]); const closed = await client.close(); expect(closed.ok).toBe(false); if (closed.ok) throw new Error("Expected unknown owner cleanup facts"); expect(closed.error.cleanupIncomplete).toBe(true); expect(closed.error.cleanupResources).toEqual([ "run:known", "run:unknown", ]); } finally { await client.close(); } }); it("projects native cancellation output and incomplete cleanup to callers", async () => { const cancellationReason = { source: "caller", token: "exact reason" }; const capture = { stdout: "partial output", stderr: "partial diagnostic", stdoutBytes: 14, stderrBytes: 19, exitCode: null, signal: "SIGKILL", truncated: false, }; const runner: NativeCommandRunner = { async run(tool) { return err( new NativeCommandFailure( tool, "cancelled", null, { cancellation: { reason: cancellationReason } }, capture, "process group cleanup was not confirmed", ["process-group:1234"], ), ); }, }; const result = await new NativeMacOSProvider({}, runner, "darwin") .createClient(machoTarget("/private/fixture")) .execute("list_architectures", {}); expect(result.ok).toBe(false); if (result.ok) return; expect(result.error.cause).toBe(cancellationReason); expect(result.error.capturedOutput).toMatchObject({ stdout: "partial output", stderr: "partial diagnostic", stdout_bytes: 14, stderr_bytes: 19, signal: "SIGKILL", truncated: false, }); expect(result.error.cleanup).toEqual({ reason: "process group cleanup was not confirmed", resources: ["process-group:1234"], }); expect(projectAnalysisError(result.error).details).toMatchObject({ captured_output: { stdout_bytes: 14, stderr_bytes: 19, signal: "SIGKILL", }, cleanup: "incomplete", resources: ["process-group:1234"], }); }); it.each([ ["timeout", "AnalysisTimeoutError", "provider_timeout"], ["output-limit", "AnalysisResourceConstraintError", "resource_constraint"], ] as const)( "projects native command %s as a typed resource failure", async (reason, tag, code) => { const runner: NativeCommandRunner = { async run(tool) { return err(new NativeCommandFailure(tool, reason)); }, }; const result = await new NativeMacOSProvider({}, runner, "darwin") .createClient(machoTarget("/private/fixture")) .execute("list_architectures", {}); expect(result.ok).toBe(false); if (result.ok) return; expect(result.error._tag).toBe(tag); expect(projectAnalysisError(result.error).code).toBe(code); }, ); }); describe("native macOS provider inspection", () => { it("normalizes comprehensive Mach-O inspection with exact bounded provenance", async () => { const runner = new FixtureRunner(); const client = new NativeMacOSProvider({}, runner, "darwin").createClient( machoTarget("/private/fixture"), ); const execution = await client.execute("inspect_macho", {}); if (!execution.ok) throw execution.error; expect(execution.value.provider.id).toBe("native-macos"); expect(execution.value.result).toMatchObject({ format: "mach-o-universal", word_size: 64, uuid: "01234567-89AB-CDEF-0123-456789ABCDEF", architectures: { total: 2, exhaustive: true }, dependencies: { items: [{ path: "/usr/lib/libSystem.B.dylib", kind: "LC_LOAD_DYLIB" }], }, segments: { items: [ { name: "__TEXT", initial_permissions: { read: true, write: false, execute: true }, sections: { items: [{ segment: "__TEXT", name: "__text" }] }, }, ], }, imports: { exhaustive: false }, exports: { exhaustive: false }, }); expect(JSON.stringify(execution.value.result)).not.toContain( "/private/fixture", ); expect(execution.value.locations).toContainEqual({ kind: "file-offset-range", start: 16384, end: 20480, }); }); it("preserves 64-bit addresses and alignment from captured otool output", async () => { const runner = new FixtureRunner({ otool: await fixture("otool-high-address.txt"), }); const client = new NativeMacOSProvider({}, runner, "darwin").createClient( machoTarget("/private/fixture"), ); const execution = await client.execute("inspect_macho", {}); if (!execution.ok) throw execution.error; expect(execution.value.result).toMatchObject({ segments: { items: [ { vm_address: "0xfffffff007004001", vm_size: 4096, file_offset: 0, file_size: 185, sections: { items: [ { address: "0xfffffff0070040b9", size: 1, file_offset: 184, alignment: 1, }, ], }, }, ], }, }); }); it("keeps Mach-O inspection available when optional vtool is unavailable", async () => { const client = new NativeMacOSProvider( {}, new VtoolFailingRunner("unavailable"), "darwin", ).createClient(machoTarget("/private/fixture")); const execution = await client.execute("inspect_macho", {}); if (!execution.ok) throw execution.error; expect(execution.value.limitations).toEqual( expect.arrayContaining([expect.stringMatching(/vtool.*unavailable/iu)]), ); expect(execution.value.result).toMatchObject({ architectures: { total: 2 }, provenance: expect.not.arrayContaining([ expect.objectContaining({ tool: "vtool" }), ]), }); }); it("propagates optional vtool failures other than unavailability", async () => { const client = new NativeMacOSProvider( {}, new VtoolFailingRunner("io"), "darwin", ).createClient(machoTarget("/private/fixture")); const execution = await client.execute("inspect_macho", {}); expect(execution).toMatchObject({ ok: false, error: { _tag: "ProviderAdapterError" }, }); }); it("supports architectures, signatures, plists, and ordered demangling", async () => { directory = await createTestTempDirectory("rea-native-"); const app = join(directory, "Fixture.app"); const executable = join(app, "Contents/MacOS/Fixture"); await mkdir(join(app, "Contents/MacOS"), { recursive: true }); await writeFile(executable, "fixture"); await writeFile(join(app, "Contents/Info.plist"), "fixture"); const externalPlist = join(directory, "Outside.plist"); await writeFile(externalPlist, "fixture"); const client = new NativeMacOSProvider( {}, new FixtureRunner(), "darwin", ).createClient(await nativeMachoTargetForFile(executable, app)); const architectures = await client.execute("list_architectures", {}); expect(architectures.ok && architectures.value.result).toMatchObject({ architectures: { total: 2 }, }); const signature = await client.execute("inspect_signature", {}); expect(signature.ok && signature.value.result).toMatchObject({ signed: true, identifier: "com.example.fixture", hardened_runtime: true, entitlements: { "com.apple.security.app-sandbox": true }, }); const defaultPlist = await client.execute("inspect_plist", {}); expect(defaultPlist.ok && defaultPlist.value.result).toMatchObject({ source_path: join(app, "Contents/Info.plist"), }); const plist = await client.execute("inspect_plist", { path: externalPlist, }); expect(plist.ok && plist.value.result).toMatchObject({ bundle: { identifier: "com.example.fixture", version: "42" }, source_path: externalPlist, }); const demangled = await client.execute("demangle_swift", { symbols: ["$s4Test3fooyyF", "plain_symbol"], }); expect(demangled.ok && demangled.value.result).toMatchObject({ symbols: [ { input: "$s4Test3fooyyF", status: "demangled" }, { input: "plain_symbol", status: "unchanged" }, ], }); }); }); describe("native macOS provider raw results", () => { it("records command provenance once, in the normalized result", async () => { directory = await createTestTempDirectory("rea-native-"); const app = join(directory, "Fixture.app"); const executable = join(app, "Contents/MacOS/Fixture"); await mkdir(join(app, "Contents/MacOS"), { recursive: true }); await writeFile(executable, "fixture"); await writeFile(join(app, "Contents/Info.plist"), "fixture"); const client = new NativeMacOSProvider( {}, new FixtureRunner(), "darwin", ).createClient(await nativeMachoTargetForFile(executable, app)); const requests: readonly (readonly [ Parameters[0], Parameters[1], ])[] = [ ["inspect_macho", {}], ["list_architectures", {}], ["inspect_signature", {}], ["inspect_plist", {}], ["demangle_swift", { symbols: ["$s4Test3fooyyF", "plain_symbol"] }], ]; for (const [operation, parameters] of requests) { const execution = await client.execute(operation, parameters); if (!execution.ok) throw execution.error; expect(execution.value.rawResult, operation).toBeNull(); expect(execution.value.result, operation).toMatchObject({ provenance: expect.arrayContaining([ expect.objectContaining({ tool: expect.any(String) }), ]), }); } }); }); describe("native plist defaults for iOS-style bundles", () => { it("defaults to the Info.plist the bundle program was resolved from", async () => { directory = await createTestTempDirectory("rea-native-flat-"); const app = join(directory, "Flat.app"); const executable = join(app, "Flat"); await mkdir(app, { recursive: true }); await writeFile(executable, "fixture"); await writeFile(join(app, "Info.plist"), "fixture"); const client = new NativeMacOSProvider( {}, new FixtureRunner(), "darwin", ).createClient({ ...machoTarget(executable, app), bundleInfoPlist: join(app, "Info.plist"), }); const plist = await client.execute("inspect_plist", {}); expect(plist.ok && plist.value.result).toMatchObject({ source_path: join(app, "Info.plist"), }); }); }); /** Emit entitlements whose dictionary also holds a legal `__proto__` key. */ class PrototypeEntitlementsRunner extends FixtureRunner { override async run(tool: string, arguments_: readonly string[]) { const result = await super.run(tool, arguments_); if (!result.ok || !arguments_.includes("--entitlements")) return result; const stdout = 'com.apple.security.app-sandbox__proto__com.apple.security.get-task-allow'; return ok({ ...result.value, stdout, stdoutBytes: Buffer.byteLength(stdout), }); } } describe("native signature entitlements", () => { it("reports entitlement entries keyed __proto__ that the result omits", async () => { directory = await createTestTempDirectory("rea-signature-entitlements-"); const executable = join(directory, "fixture"); await writeFile(executable, "fixture"); const client = new NativeMacOSProvider( {}, new PrototypeEntitlementsRunner(), "darwin", ).createClient(await nativeMachoTargetForFile(executable)); const signature = await client.execute("inspect_signature", {}); if (!signature.ok) throw signature.error; expect(signature.value.result).toMatchObject({ entitlements: { "com.apple.security.app-sandbox": true }, }); expect(signature.value.limitations).toContain( "Entitlements: 1 dictionary entry keyed __proto__ was omitted because REA results cannot represent that key.", ); }); }); /** Fail every codesign call nonzero after writing more than one excerpt. */ class VerboseCodeSignFailureRunner extends FixtureRunner { override async run(tool: string, arguments_: readonly string[]) { const result = await super.run(tool, arguments_); if (!result.ok || tool !== "codesign") return result; const stderr = "e".repeat(NATIVE_FAILURE_DIAGNOSTIC_BYTES * 2); return ok({ ...result.value, stderr, stderrBytes: Buffer.byteLength(stderr), exitCode: 1, }); } } describe("native signature failure diagnostics", () => { it("bounds accepted nonzero codesign output once it becomes a failure", async () => { directory = await createTestTempDirectory("rea-signature-failure-"); const executable = join(directory, "fixture"); await writeFile(executable, "fixture"); const client = new NativeMacOSProvider( {}, new VerboseCodeSignFailureRunner(), "darwin", ).createClient(await nativeMachoTargetForFile(executable)); const signature = await client.execute("inspect_signature", {}); if (signature.ok) throw new Error("A failed codesign run must fail"); expect(signature.error.capturedOutput).toMatchObject({ stderr: "e".repeat(NATIVE_FAILURE_DIAGNOSTIC_BYTES), stderr_bytes: NATIVE_FAILURE_DIAGNOSTIC_BYTES * 2, truncated: true, }); }); }); describe("native dispatch metadata error results", () => { it("preserves tagged cancellation and integrity failures", async () => { directory = await createTestTempDirectory("rea-dispatch-errors-"); const targetPath = join(directory, "fixture.macho"); await writeFile(targetPath, "bound target bytes"); const client = new NativeMacOSProvider( {}, new FixtureRunner(), "darwin", ).createClient(machoTarget(targetPath)); const controller = new AbortController(); const cancelled = client.execute( "inspect_native_dispatch_metadata", {}, { signal: controller.signal }, ); controller.abort(); const cancellationResult = await cancelled; expect(!cancellationResult.ok && cancellationResult.error._tag).toBe( "AnalysisCancelledError", ); const mismatchedClient = new NativeMacOSProvider( {}, new FixtureRunner(), "darwin", ).createClient({ ...machoTarget(targetPath), sha256: "f".repeat(64) }); const integrityResult = await mismatchedClient.execute( "inspect_native_dispatch_metadata", {}, ); expect(!integrityResult.ok && integrityResult.error._tag).toBe( "EvidenceIntegrityError", ); }); }); describe("native macOS provider failures and parsing", () => { it("classifies unavailable, malformed, command failure, and cancellation", async () => { const unavailable = new NativeMacOSProvider( {}, new FailingRunner("unavailable"), "darwin", ).createClient(machoTarget("/fixture")); const unavailableResult = await unavailable.execute( "list_architectures", {}, ); expect(!unavailableResult.ok && unavailableResult.error._tag).toBe( "AnalysisCapabilityUnavailableError", ); if (unavailableResult.ok) throw new Error("Expected an unavailable tool"); expect(projectAnalysisError(unavailableResult.error).message).toBe( "lipo is unavailable through xcrun.", ); const malformed = new NativeMacOSProvider( {}, new FixtureRunner({ lipo: "malformed" }), "darwin", ).createClient(machoTarget("/fixture")); const malformedResult = await malformed.execute("list_architectures", {}); expect(!malformedResult.ok && malformedResult.error._tag).toBe( "AnalysisOutputError", ); for (const [reason, tag] of [ ["io", "ProviderAdapterError"], ["cancelled", "AnalysisCancelledError"], ] as const) { const client = new NativeMacOSProvider( {}, new FailingRunner(reason), "darwin", ).createClient(machoTarget("/fixture")); const result = await client.execute("list_architectures", {}); expect(!result.ok && result.error._tag).toBe(tag); } }); it.each([ { ...machoTarget("/fixture.dll"), format: "pe" as const, executableRole: "shared-library" as const, managed: false, }, { ...machoTarget("/fixture.elf"), format: "elf" as const }, ])( "refuses to list architectures of a $format target without running lipo", async (target) => { const runner = new CountingRunner(); const result = await new NativeMacOSProvider({}, runner, "darwin") .createClient(target) .execute("list_architectures", {}); if (result.ok) throw new Error("Expected a non-Mach-O refusal"); expect(result.error._tag).toBe("AnalysisCapabilityUnavailableError"); expect(projectAnalysisError(result.error)).toMatchObject({ code: "capability_unavailable", details: { reason: "Active artifact is not Mach-O." }, }); expect(runner.calls).toBe(0); }, ); it("classifies pre-aborted requests before operation or runner discovery", async () => { const controller = new AbortController(); controller.abort(); const runner = new CountingRunner(); const client = new NativeMacOSProvider({}, runner, "linux").createClient( machoTarget("/fixture"), ); const result = await client.execute( "list_architectures", {}, { signal: controller.signal, }, ); expect(!result.ok && result.error._tag).toBe("AnalysisCancelledError"); expect(runner.calls).toBe(0); }); it("parses thin and universal lipo fixtures", async () => { expect(parseLipoOutput(await fixture("lipo-thin.txt"))).toMatchObject({ format: "thin", architectures: [{ name: "arm64" }], }); expect(parseLipoOutput(await fixture("lipo-fat.txt"))).toMatchObject({ format: "fat", architectures: [ { name: "x86_64", file_offset: 16384, size: 4096 }, { name: "arm64", file_offset: 32768, size: 8192 }, ], }); }); it("rejects trailing text in lipo numeric fields instead of truncating", () => { const output = [ "architecture arm64", " cputype CPU_TYPE_ARM64", " cpusubtype CPU_SUBTYPE_ARM64_ALL", " offset 16384 trailing-junk", " size 0x10", " align 2^14 (16384)", ].join("\n"); expect(parseLipoOutput(output).architectures).toMatchObject([ { name: "arm64", file_offset: null, size: null, alignment: 16384 }, ]); }); it("requires the full alignment field to use a recognized spelling", () => { const output = [ "architecture arm64", " offset 0", " size 16", " align 2^14 (16384)", ].join("\n"); expect( parseLipoOutput(output.replace("(16384)", "(8192)")).architectures[0] ?.alignment, ).toBeNull(); expect( parseLipoOutput(output.replace("2^14 (16384)", "2^14")).architectures[0] ?.alignment, ).toBe(16384); }); it("reads hardened runtime only from CodeDirectory flags", () => { expect( parseCodeSignature("Diagnostic: runtime policy unavailable", false) .hardened_runtime, ).toBeNull(); expect( parseCodeSignature( "CodeDirectory v=20500 flags=0x10000(runtime) hashes=10+7", false, ).hardened_runtime, ).toBe(true); expect( parseCodeSignature( "CodeDirectory v=20500 flags=0x2(adhoc,hard) runtime=1", false, ).hardened_runtime, ).toBe(false); }); }); describe("native signature inspection target scope", () => { // This provider's inspect_signature path is scoped to Mach-O targets; on // another container a codesign verdict cannot see a format-level signature // such as a xar pkg signature. it.each([ { path: "/fixture.pkg", sha256: "0".repeat(64), kind: "archive" as const, format: "pkg" as const, }, { ...machoTarget("/fixture.elf"), format: "elf" as const, }, ])( "refuses inspect_signature on a $format target without running codesign", async (target) => { const runner = new CountingRunner(); const result = await new NativeMacOSProvider({}, runner, "darwin") .createClient(target) .execute("inspect_signature", {}); if (result.ok) throw new Error("Expected a non-Mach-O refusal"); expect(result.error._tag).toBe("AnalysisCapabilityUnavailableError"); expect(projectAnalysisError(result.error)).toMatchObject({ code: "capability_unavailable", }); expect(runner.calls).toBe(0); }, ); }); class FailingRunner implements NativeCommandRunner { constructor(private readonly reason: NativeCommandFailure["reason"]) {} run(tool: string) { return Promise.resolve(err(new NativeCommandFailure(tool, this.reason))); } } class VtoolFailingRunner implements NativeCommandRunner { readonly #fixture = new FixtureRunner(); constructor(private readonly reason: NativeCommandFailure["reason"]) {} run(tool: string, arguments_: readonly string[]) { return tool === "vtool" ? Promise.resolve(err(new NativeCommandFailure(tool, this.reason))) : this.#fixture.run(tool, arguments_); } } class CountingRunner implements NativeCommandRunner { calls = 0; run(tool: string) { this.calls += 1; return Promise.resolve(err(new NativeCommandFailure(tool, "unavailable"))); } } it("does not reopen a resolved preparation cleanup marker or invent absent compiler output facts", async () => { const marker = new ProviderCleanupError( "process-ownership", ["owned helper root"], { reason: "controlled close failure" }, ); const capturedOutput = { stdout: "compiler output", stderr: "�", stderr_bytes: 1, truncated: false, }; const inspection = new DarwinProcessOwnershipInspectionError( "controlled preparation failure", { capturedOutput, cleanupFailure: marker }, ); marker.resolveCleanup(); const failure = new NativeCommandFailure("swift-demangle", "io", null, { cause: inspection, }); const runner: NativeCommandRunner = { run: async () => err(failure) }; const client = new NativeMacOSProvider({}, runner, "darwin").createClient( machoTarget("/fixture"), ); try { const result = await client.execute("demangle_swift", { symbols: ["fixture"], }); if (result.ok) throw new Error("Expected preparation failure"); expect(result.error.cleanupIncomplete).toBe(false); expect(result.error.capturedOutput).toEqual(capturedOutput); expect(result.error.capturedOutput).not.toHaveProperty("stdout_bytes"); expect(result.error.capturedOutput).not.toHaveProperty("exit_code"); } finally { await client.close(); } }); it("retains an owned preparation cleanup marker when the command deadline expires", async () => { const marker = new ProviderCleanupError( "process-ownership", ["owned helper root"], { reason: "controlled cleanup failure" }, ); const reason = new Error("command deadline"); const inspection = new DarwinProcessOwnershipInspectionError( "cancelled preparation", { cancellation: { reason }, cleanupFailure: marker }, ); const owned = new OwnedCommandFailure( "timeout", "command preparation timed out", null, null, { cause: inspection, preparationFailure: inspection }, ); const failure = new NativeCommandFailure("swift-demangle", "timeout", null, { cause: owned, }); const runner: NativeCommandRunner = { run: async () => err(failure) }; const client = new NativeMacOSProvider({}, runner, "darwin").createClient( machoTarget("/fixture"), ); try { const result = await client.execute("demangle_swift", { symbols: ["fixture"], }); if (result.ok) throw new Error("Expected preparation timeout"); expect(result.error).toMatchObject({ _tag: "AnalysisTimeoutError", cleanupIncomplete: true, }); expect(result.error.cause).toBe(reason); marker.resolveCleanup(); expect(result.error.cleanupIncomplete).toBe(false); } finally { await client.close(); } }); it("reports the selected command deadline when preparation times out", async () => { const marker = new ProviderCleanupError( "process-ownership", ["owned helper root"], { reason: "controlled cleanup failure" }, ); const reason = new Error("command deadline"); const inspection = new DarwinProcessOwnershipInspectionError( "cancelled preparation", { cancellation: { reason }, cleanupFailure: marker }, ); const owned = new OwnedCommandFailure( "timeout", "command preparation timed out", null, null, { cause: inspection, preparationFailure: inspection }, ); const failure = new NativeCommandFailure("swift-demangle", "timeout", null, { cause: owned, }); const runner: NativeCommandRunner = { run: async () => err(failure) }; const client = new NativeMacOSProvider({}, runner, "darwin").createClient( machoTarget("/fixture"), ); try { const result = await client.execute("demangle_swift", { symbols: ["fixture"], native_command_timeout_ms: 1_500, }); if (result.ok) throw new Error("Expected preparation timeout"); expect(result.error).toMatchObject({ _tag: "AnalysisTimeoutError", cleanupIncomplete: true, }); expect(result.error.cause).toBe(reason); const projected = projectAnalysisError(result.error); expect(projected.message).toContain("of 1500 ms"); expect(projected.message).toContain("native_command_timeout_ms"); expect(projected.remediation.action).toContain("above 1500"); expect(projected.remediation.action).not.toContain("above 60000"); expect(projected.remediation.action).toContain( `${String(NATIVE_COMMAND_OUTPUT_BUDGET_BYTES)}-byte command output allowance`, ); marker.resolveCleanup(); expect(result.error.cleanupIncomplete).toBe(false); } finally { await client.close(); } });