import { spawn } from "node:child_process"; import { createHash } from "node:crypto"; import { closeSync, constants, fchmodSync, openSync, watch } from "node:fs"; import { lstat, mkdir, mkdtemp, open, readFile, readdir, readlink, rm, writeFile, } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; import { afterEach, describe, expect, it } from "vitest"; const roots: string[] = []; afterEach(async () => { await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true }))); }); describe("provider guardian transaction protocol", () => { it("encodes a v2 guard without accepting filesystem paths or identities", async () => { const protocol = await import("../../scripts/lib/provider-guardian-protocol.ts") as Record; expect(protocol.encodeProviderGuardianGuard).toBeTypeOf("function"); const encode = protocol.encodeProviderGuardianGuard as (input: Readonly<{ kind: "vulnerability"; nonce: Buffer; deadlineEpochMs: number; }>) => Buffer; const nonce = Buffer.alloc(32, 0x5a); const encoded = encode({ kind: "vulnerability", nonce, deadlineEpochMs: 1_800_000_000_000, }); expect(encoded.readUInt32BE(0)).toBe(encoded.byteLength - 4); expect(JSON.parse(encoded.subarray(4).toString("utf8"))).toEqual({ type: "guard", version: 2, kind: "vulnerability", nonce: "5a".repeat(32), deadlineEpochMs: 1_800_000_000_000, }); expect( (protocol.providerGuardianSealedTempLeaf as (kind: "vulnerability", value: Buffer) => string)( "vulnerability", nonce, ), ).toBe(`.vulnerability-report.json.guardian-${"5a".repeat(16)}.tmp`); expect( (protocol.providerGuardianRawStagingLeaf as ( kind: "vulnerability", value: Buffer, ) => string)("vulnerability", nonce), ).toBe(`.vulnerability-report.json.guardian-${"5a".repeat(16)}.raw.tmp`); }); it("decodes only the exact canonical v2 guard within the lease window", async () => { const protocol = await import("../../scripts/lib/provider-guardian-protocol.ts") as Record; expect(protocol.decodeProviderGuardianGuard).toBeTypeOf("function"); const decode = protocol.decodeProviderGuardianGuard as ( payload: Buffer, options: Readonly<{ nowEpochMs: number; maxLeaseMs: number }>, ) => Readonly<{ kind: string; nonce: Buffer; deadlineEpochMs: number }>; const canonical = JSON.stringify({ type: "guard", version: 2, kind: "provenance", nonce: "3c".repeat(32), deadlineEpochMs: 5_000, }); expect(decode(Buffer.from(canonical), { nowEpochMs: 1_000, maxLeaseMs: 5_000 })) .toEqual({ kind: "provenance", nonce: Buffer.alloc(32, 0x3c), deadlineEpochMs: 5_000, }); for (const invalid of [ canonical.replace('"nonce":', '"nonce":"3c'.concat('"'.repeat(0), ',"nonce":')), canonical.replace(/\}$/u, ',"rawIno":9}'), `${canonical} `, canonical.replace("5000", "1000"), canonical.replace("5000", "7000"), canonical.replace("3c".repeat(32), "3c".repeat(31)), ]) { expect(() => decode(Buffer.from(invalid), { nowEpochMs: 1_000, maxLeaseMs: 5_000 })) .toThrow(/provider guardian/u); } expect(() => decode(Buffer.from([0xff]), { nowEpochMs: 1_000, maxLeaseMs: 5_000 })) .toThrow(/provider guardian/u); }); it("authenticates READY, publish, PUBLISHED, and v2 commit with exact fields", async () => { const protocol = await import("../../scripts/lib/provider-guardian-protocol.ts") as Record; for (const name of [ "decodeProviderGuardianReady", "encodeProviderGuardianPublish", "decodeProviderGuardianPublish", "encodeProviderGuardianPublished", "decodeProviderGuardianPublished", ]) { expect(protocol[name], name).toBeTypeOf("function"); } const nonce = Buffer.alloc(32, 0x7a); const readyPayload = Buffer.from(JSON.stringify({ type: "ready", version: 2, nonce: "7a".repeat(32), rawDev: 12, rawIno: 34, sealedTempLeaf: ".vulnerability-report.json.guardian-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.tmp", sealedDev: 56, sealedIno: 78, })); const decodeReady = protocol.decodeProviderGuardianReady as ( payload: Buffer, expectedNonce: Buffer, ) => Record; expect(decodeReady(readyPayload, nonce)).toEqual({ nonce, rawDev: 12, rawIno: 34, sealedTempLeaf: ".vulnerability-report.json.guardian-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.tmp", sealedDev: 56, sealedIno: 78, }); expect(() => decodeReady(readyPayload, Buffer.alloc(32, 0x7b))) .toThrow(/authentication/u); const encodePublish = protocol.encodeProviderGuardianPublish as (input: Record) => Buffer; const publishFrame = encodePublish({ nonce, sealedDev: 56, sealedIno: 78, size: 123, sha256: "ab".repeat(32), }); expect(JSON.parse(publishFrame.subarray(4).toString("utf8"))).toEqual({ type: "publish", version: 2, nonce: "7a".repeat(32), sealedDev: 56, sealedIno: 78, size: 123, sha256: "ab".repeat(32), }); const decodePublish = protocol.decodeProviderGuardianPublish as ( payload: Buffer, expectedNonce: Buffer, ) => Record; expect(decodePublish(publishFrame.subarray(4), nonce)).toEqual({ nonce, sealedDev: 56, sealedIno: 78, size: 123, sha256: "ab".repeat(32), }); const encodePublished = protocol.encodeProviderGuardianPublished as ( input: Readonly<{ nonce: Buffer; sealedDev: number; sealedIno: number }>, ) => Buffer; const published = encodePublished({ nonce, sealedDev: 56, sealedIno: 78 }); const decodePublished = protocol.decodeProviderGuardianPublished as ( payload: Buffer, expectedNonce: Buffer, expectedIdentity: Readonly<{ dev: number; ino: number }>, ) => void; expect(() => decodePublished(published.subarray(4), nonce, { dev: 56, ino: 78 })) .not.toThrow(); const encodeCommit = protocol.encodeProviderGuardianCommit as (value: Buffer) => Buffer; expect(JSON.parse(encodeCommit(nonce).subarray(4).toString("utf8"))).toEqual({ type: "commit", version: 2, nonce: "7a".repeat(32), }); }); it("creates and returns owned raw and sealed-temp identities before READY", async () => { const { decodeProviderGuardianReady, encodeProviderGuardianGuard, } = await import("../../scripts/lib/provider-guardian-protocol.ts"); const workspace = await createWorkspace("provider-guardian-ready-"); const rawPath = path.join( workspace, "provider-evidence/untrusted/vulnerability-report.json", ); const nonce = Buffer.alloc(32, 0x4d); const child = spawnGuardian(workspace, nonce); const completion = waitForChild(child); const readyPayload = readFrame(child.stdout!); child.stdin!.write(encodeProviderGuardianGuard({ kind: "vulnerability", nonce, deadlineEpochMs: Date.now() + 2_000, })); const ready = decodeProviderGuardianReady( await within(readyPayload, 1_000, "guardian READY"), nonce, ); const raw = await lstat(rawPath); const sealedTempPath = path.join(workspace, "provider-evidence", ready.sealedTempLeaf); const sealedTemp = await lstat(sealedTempPath); expect({ dev: raw.dev, ino: raw.ino, mode: raw.mode & 0o777 }).toEqual({ dev: ready.rawDev, ino: ready.rawIno, mode: 0o600, }); expect({ dev: sealedTemp.dev, ino: sealedTemp.ino, mode: sealedTemp.mode & 0o777 }) .toEqual({ dev: ready.sealedDev, ino: ready.sealedIno, mode: 0o600 }); child.stdin!.end(); await expect(within(completion, 1_000, "guardian abort")).resolves.toEqual({ code: 125, signal: null, }); await expect(lstat(rawPath)).rejects.toMatchObject({ code: "ENOENT" }); await expect(lstat(sealedTempPath)).rejects.toMatchObject({ code: "ENOENT" }); await expect(readdir(path.join(workspace, "provider-evidence/untrusted"))).resolves.toEqual([]); }); it("creates no evidence for empty or truncated guard input", async () => { const { startProviderGuardian } = await import( "../../scripts/lib/provider-guardian-client.ts" ); for (const [label, input, expectedCode] of [ ["empty", undefined, 125], ["truncated-prefix", Buffer.from([0, 0]), 126], ] as const) { const workspace = await createWorkspace(`provider-guardian-${label}-`); const child = spawnGuardian(workspace); const completion = waitForChild(child); if (input) child.stdin!.end(input); else child.stdin!.end(); await expect(within(completion, 1_000, `${label} guardian EOF`)).resolves.toEqual({ code: expectedCode, signal: null, }); await expect(readdir(path.join(workspace, "provider-evidence/untrusted"))).resolves.toEqual([]); await expect(readdir(path.join(workspace, "provider-evidence"))).resolves.toEqual([ "untrusted", ]); const retry = await startProviderGuardian({ kind: "vulnerability", workspaceRoot: workspace, leaseMs: 3_000, guardianScript: path.resolve("scripts/lib/provider-raw-guardian.ts"), }); await retry.abort(); await expect(readdir(path.join(workspace, "provider-evidence/untrusted"))) .resolves.toEqual([]); await expect(readdir(path.join(workspace, "provider-evidence"))) .resolves.toEqual(["untrusted"]); } }); it.each(["raw", "sealed"] as const)( "cleans the independently bound %s private alias when peer bootstrap validation fails", async (boundKind) => { const workspace = await createWorkspace(`provider-guardian-partial-bootstrap-${boundKind}-`); const nonce = Buffer.alloc(32, boundKind === "raw" ? 0x71 : 0x72); const fixture = spawnGuardianWithPrivateModes(workspace, nonce, { rawMode: boundKind === "raw" ? 0o600 : 0o400, sealedMode: boundKind === "sealed" ? 0o600 : 0o400, }); await expect(within( waitForChild(fixture.child), 1_000, `${boundKind} partial bootstrap exit`, )).resolves.toEqual({ code: 126, signal: null }); const boundPath = boundKind === "raw" ? fixture.rawStagingPath : fixture.sealedTempPath; const invalidPath = boundKind === "raw" ? fixture.sealedTempPath : fixture.rawStagingPath; await expect(lstat(boundPath)).rejects.toMatchObject({ code: "ENOENT" }); await expect(lstat(invalidPath)).resolves.toEqual(expect.objectContaining({ mode: expect.any(Number), })); await rm(invalidPath); await expect(readdir(path.join(workspace, "provider-evidence/untrusted"))) .resolves.toEqual([]); await expect(readdir(path.join(workspace, "provider-evidence"))) .resolves.toEqual(["untrusted"]); }, ); it("cleans guardian-created objects when the parent closes immediately after guard", async () => { const { encodeProviderGuardianGuard } = await import( "../../scripts/lib/provider-guardian-protocol.ts" ); const workspace = await createWorkspace("provider-guardian-parent-startup-death-"); const nonce = Buffer.alloc(32, 0x2b); const child = spawnGuardian(workspace, nonce); const completion = waitForChild(child); child.stdin!.end(encodeProviderGuardianGuard({ kind: "vulnerability", nonce, deadlineEpochMs: Date.now() + 2_000, })); await expect(within(completion, 1_000, "guardian parent startup death")) .resolves.toEqual({ code: 125, signal: null }); await expect(readdir(path.join(workspace, "provider-evidence/untrusted"))).resolves.toEqual([]); await expect(readdir(path.join(workspace, "provider-evidence"))).resolves.toEqual(["untrusted"]); }); it("publishes the pinned sealed inode and preserves it only after commit EOF", async () => { const { decodeProviderGuardianPublished, decodeProviderGuardianReady, encodeProviderGuardianCommit, encodeProviderGuardianGuard, encodeProviderGuardianPublish, } = await import("../../scripts/lib/provider-guardian-protocol.ts"); const workspace = await createWorkspace("provider-guardian-publish-"); const evidenceRoot = path.join(workspace, "provider-evidence"); const rawPath = path.join(evidenceRoot, "untrusted/vulnerability-report.json"); const sealedPath = path.join(evidenceRoot, "vulnerability-report.json"); const nonce = Buffer.alloc(32, 0x62); const child = spawnGuardian(workspace, nonce); const completion = waitForChild(child); const readyPayload = readFrame(child.stdout!); child.stdin!.write(encodeProviderGuardianGuard({ kind: "vulnerability", nonce, deadlineEpochMs: Date.now() + 3_000, })); const ready = decodeProviderGuardianReady( await within(readyPayload, 1_000, "guardian READY"), nonce, ); const sealedTempPath = path.join(evidenceRoot, ready.sealedTempLeaf); const bytes = Buffer.from('{"validated":true}\n'); const handle = await open( sealedTempPath, constants.O_WRONLY | constants.O_NOFOLLOW, ); try { const before = await handle.stat(); expect({ dev: before.dev, ino: before.ino }).toEqual({ dev: ready.sealedDev, ino: ready.sealedIno, }); await handle.writeFile(bytes); await handle.chmod(0o400); await handle.sync(); } finally { await handle.close(); } const publishedPayload = readFrame(child.stdout!); child.stdin!.write(encodeProviderGuardianPublish({ nonce, sealedDev: ready.sealedDev, sealedIno: ready.sealedIno, size: bytes.byteLength, sha256: createHash("sha256").update(bytes).digest("hex"), })); decodeProviderGuardianPublished( await within(publishedPayload, 1_000, "guardian PUBLISHED"), nonce, { dev: ready.sealedDev, ino: ready.sealedIno }, ); await expect(lstat(sealedTempPath)).rejects.toMatchObject({ code: "ENOENT" }); const published = await lstat(sealedPath); expect({ dev: published.dev, ino: published.ino, mode: published.mode & 0o777 }) .toEqual({ dev: ready.sealedDev, ino: ready.sealedIno, mode: 0o400 }); await expect(readFile(sealedPath)).resolves.toEqual(bytes); child.stdin!.write(encodeProviderGuardianCommit(nonce)); await new Promise((resolve) => setTimeout(resolve, 50)); expect(child.exitCode).toBeNull(); child.stdin!.end(); await expect(within(completion, 1_000, "guardian commit EOF")).resolves.toEqual({ code: 0, signal: null, }); await expect(lstat(rawPath)).rejects.toMatchObject({ code: "ENOENT" }); await expect(readFile(sealedPath)).resolves.toEqual(bytes); }); it("owns raw and sealed publication through the authenticated client lease", async () => { const client = await import("../../scripts/lib/provider-guardian-client.ts") as Record; expect(client.startProviderGuardian).toBeTypeOf("function"); const start = client.startProviderGuardian as (input: Readonly<{ kind: "vulnerability"; workspaceRoot: string; leaseMs: number; guardianScript: string; }>) => Promise; sealedPath: string; sealedTempPath: string; sealedIdentity: Readonly<{ dev: number; ino: number }>; publish(bytes: Buffer): Promise; commit(): Promise; }>>; const workspace = await createWorkspace("provider-guardian-client-v2-"); const guardianScript = path.resolve("scripts/lib/provider-raw-guardian.ts"); const lease = await start({ kind: "vulnerability", workspaceRoot: workspace, leaseMs: 3_000, guardianScript, }); expect((await readFile(`/proc/${lease.pid}/cmdline`)).toString("utf8") .split("\0").filter(Boolean)).toEqual([process.execPath, guardianScript]); await expect(readlink(`/proc/${lease.pid}/fd/3`)).resolves.toBe( path.join(workspace, "provider-evidence/untrusted"), ); await expect(readlink(`/proc/${lease.pid}/fd/4`)).resolves.toBe( path.join(workspace, "provider-evidence"), ); const rawMetadata = await lstat(lease.rawPath); expect({ dev: rawMetadata.dev, ino: rawMetadata.ino }).toEqual(lease.rawIdentity); const sealedTempMetadata = await lstat(lease.sealedTempPath); expect({ dev: sealedTempMetadata.dev, ino: sealedTempMetadata.ino }) .toEqual(lease.sealedIdentity); const bytes = Buffer.from('{"validated":"client"}\n'); await lease.publish(bytes); await expect(lstat(lease.sealedTempPath)).rejects.toMatchObject({ code: "ENOENT" }); await expect(readFile(lease.sealedPath)).resolves.toEqual(bytes); await lease.commit(); await expect(lstat(lease.rawPath)).rejects.toMatchObject({ code: "ENOENT" }); await expect(readFile(lease.sealedPath)).resolves.toEqual(bytes); }); it("cleans published evidence on EOF before commit and permits a same-workspace retry", async () => { const workspace = await createWorkspace("provider-guardian-published-eof-"); const transaction = await establishPublishedGuardian(workspace, Buffer.alloc(32, 0x31)); transaction.child.stdin!.end(); await expect(within(transaction.completion, 1_000, "published guardian EOF")) .resolves.toEqual({ code: 125, signal: null }); await assertTransactionAbsent(transaction); const { startProviderGuardian } = await import( "../../scripts/lib/provider-guardian-client.ts" ); const retry = await startProviderGuardian({ kind: "vulnerability", workspaceRoot: workspace, leaseMs: 2_000, guardianScript: path.resolve("scripts/lib/provider-raw-guardian.ts"), }); await retry.abort(); await expect(readdir(path.join(workspace, "provider-evidence/untrusted"))).resolves.toEqual([]); await expect(readdir(path.join(workspace, "provider-evidence"))).resolves.toEqual(["untrusted"]); }); it("fails closed when a separate trailing frame arrives after commit", async () => { const { encodeProviderGuardianCommit } = await import( "../../scripts/lib/provider-guardian-protocol.ts" ); const workspace = await createWorkspace("provider-guardian-commit-trailing-"); const nonce = Buffer.alloc(32, 0x47); const transaction = await establishPublishedGuardian(workspace, nonce); transaction.child.stdin!.write(encodeProviderGuardianCommit(nonce)); await new Promise((resolve) => setImmediate(resolve)); transaction.child.stdin!.write(Buffer.from([0, 0, 0, 1, 0x7b])); transaction.child.stdin!.end(); await expect(within(transaction.completion, 1_000, "guardian trailing frame")) .resolves.toEqual({ code: 126, signal: null }); await assertTransactionAbsent(transaction); }); it("uses pinned fallback identities after a guardian hard death", async () => { const { startProviderGuardian } = await import( "../../scripts/lib/provider-guardian-client.ts" ); const workspace = await createWorkspace("provider-guardian-hard-death-"); const lease = await startProviderGuardian({ kind: "vulnerability", workspaceRoot: workspace, leaseMs: 3_000, guardianScript: path.resolve("scripts/lib/provider-raw-guardian.ts"), }); process.kill(lease.pid, "SIGKILL"); await expect(within(lease.prematureExit, 1_000, "guardian hard death")) .resolves.toEqual(expect.objectContaining({ message: expect.stringMatching(/SIGKILL/u) })); await lease.abort(); await expect(lstat(lease.rawPath)).rejects.toMatchObject({ code: "ENOENT" }); await expect(lstat(lease.sealedTempPath)).rejects.toMatchObject({ code: "ENOENT" }); await expect(lstat(lease.sealedPath)).rejects.toMatchObject({ code: "ENOENT" }); }); it("cleans a linked raw inode when killed before READY and retries immediately", async () => { const { startProviderGuardian } = await import( "../../scripts/lib/provider-guardian-client.ts" ); const workspace = await createWorkspace("provider-guardian-pre-ready-death-"); const evidenceRoot = path.join(workspace, "provider-evidence"); const rawDirectory = path.join(evidenceRoot, "untrusted"); const rawPath = path.join(rawDirectory, "vulnerability-report.json"); const guardianScript = await createPausedGuardianFixture(workspace); const existingChildren = await directChildPids(process.pid); const starting = startProviderGuardian({ kind: "vulnerability", workspaceRoot: workspace, leaseMs: 3_000, guardianScript, }); const guardianPid = await waitForNewDirectChild(existingChildren); await waitForStoppedProcess(guardianPid); const rawLink = waitForDirectoryEntry(rawDirectory, path.basename(rawPath), () => { process.kill(guardianPid, "SIGKILL"); }); process.kill(guardianPid, "SIGCONT"); await within(rawLink, 1_000, "pre-READY canonical raw link"); await expect(within(starting, 2_000, "pre-READY guardian rejection")) .rejects.toThrow(/provider guardian/u); const possibleTempLeaves = (await readdir(evidenceRoot)).filter((leaf) => leaf.startsWith(".vulnerability-report.json.guardian-") ); expect(possibleTempLeaves).toEqual([]); await expect(lstat(rawPath)).rejects.toMatchObject({ code: "ENOENT" }); const retry = await startProviderGuardian({ kind: "vulnerability", workspaceRoot: workspace, leaseMs: 3_000, guardianScript: path.resolve("scripts/lib/provider-raw-guardian.ts"), }); expect(path.basename(retry.sealedTempPath)).toMatch( /^\.vulnerability-report\.json\.guardian-[0-9a-f]{32}\.tmp$/u, ); await retry.abort(); await expect(readdir(rawDirectory)).resolves.toEqual([]); await expect(readdir(evidenceRoot)).resolves.toEqual(["untrusted"]); }); it("preserves an external raw canary created after startup checks", async () => { const { startProviderGuardian } = await import( "../../scripts/lib/provider-guardian-client.ts" ); const workspace = await createWorkspace("provider-guardian-external-canary-"); const rawPath = path.join( workspace, "provider-evidence/untrusted/vulnerability-report.json", ); const { guardianScript, markerPath } = await createStalledGuardianFixture(workspace); const starting = startProviderGuardian({ kind: "vulnerability", workspaceRoot: workspace, leaseMs: 3_000, guardianScript, }); const guardianPid = Number(await waitForFile(markerPath)); const canaryBytes = Buffer.from("external-canary\n"); const canaryHandle = await open( rawPath, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW, 0o600, ); await canaryHandle.writeFile(canaryBytes); const canaryIdentity = await canaryHandle.stat(); await canaryHandle.close(); process.kill(guardianPid, "SIGKILL"); await expect(within(starting, 2_000, "external-canary guardian rejection")) .rejects.toThrow(/provider guardian/u); expect(await readFile(rawPath)).toEqual(canaryBytes); expect(await lstat(rawPath)).toMatchObject({ dev: canaryIdentity.dev, ino: canaryIdentity.ino, }); await rm(rawPath); const retry = await startProviderGuardian({ kind: "vulnerability", workspaceRoot: workspace, leaseMs: 3_000, guardianScript: path.resolve("scripts/lib/provider-raw-guardian.ts"), }); await retry.abort(); await expect(readdir(path.join(workspace, "provider-evidence/untrusted"))) .resolves.toEqual([]); await expect(readdir(path.join(workspace, "provider-evidence"))) .resolves.toEqual(["untrusted"]); }); it("allows exactly one same-kind guardian without deleting the winner", async () => { const { startProviderGuardian } = await import( "../../scripts/lib/provider-guardian-client.ts" ); const workspace = await createWorkspace("provider-guardian-concurrent-kind-"); const input = { kind: "vulnerability" as const, workspaceRoot: workspace, leaseMs: 10_000, guardianScript: path.resolve("scripts/lib/provider-raw-guardian.ts"), }; const results = await Promise.allSettled([ startProviderGuardian(input), startProviderGuardian(input), ]); const winners = results.filter((result) => result.status === "fulfilled"); const losers = results.filter((result) => result.status === "rejected"); expect(winners).toHaveLength(1); expect(losers).toHaveLength(1); const winner = winners[0]!.value; const rawMetadata = await lstat(winner.rawPath); expect({ dev: rawMetadata.dev, ino: rawMetadata.ino }).toEqual(winner.rawIdentity); await winner.abort(); await expect(readdir(path.join(workspace, "provider-evidence/untrusted"))) .resolves.toEqual([]); await expect(readdir(path.join(workspace, "provider-evidence"))) .resolves.toEqual(["untrusted"]); const retry = await startProviderGuardian(input); await retry.abort(); await expect(readdir(path.join(workspace, "provider-evidence/untrusted"))) .resolves.toEqual([]); await expect(readdir(path.join(workspace, "provider-evidence"))) .resolves.toEqual(["untrusted"]); }); it("exits nonzero after cleanup when the diagnostic stderr pipe is closed", async () => { const { decodeProviderGuardianReady, encodeProviderGuardianGuard, } = await import("../../scripts/lib/provider-guardian-protocol.ts"); const workspace = await createWorkspace("provider-guardian-closed-stderr-"); const evidenceRoot = path.join(workspace, "provider-evidence"); const rawDirectory = path.join(evidenceRoot, "untrusted"); const nonce = Buffer.alloc(32, 0x63); const child = spawnGuardian(workspace, nonce); const completion = waitForChild(child); const readyPayload = readFrame(child.stdout!); child.stdin!.write(encodeProviderGuardianGuard({ kind: "vulnerability", nonce, deadlineEpochMs: Date.now() + 3_000, })); const ready = decodeProviderGuardianReady( await within(readyPayload, 1_000, "closed-stderr guardian READY"), nonce, ); child.stderr!.destroy(); await new Promise((resolve) => child.stderr!.once("close", resolve)); child.stdin!.end(); await expect(within(completion, 1_000, "closed-stderr guardian exit")) .resolves.toEqual({ code: 125, signal: null }); await expect(lstat(path.join(rawDirectory, "vulnerability-report.json"))) .rejects.toMatchObject({ code: "ENOENT" }); await expect(lstat(path.join(evidenceRoot, ready.sealedTempLeaf))) .rejects.toMatchObject({ code: "ENOENT" }); }); it("expires an uncommitted lease only after cleaning every owned object", async () => { const { decodeProviderGuardianReady, encodeProviderGuardianGuard, } = await import("../../scripts/lib/provider-guardian-protocol.ts"); const workspace = await createWorkspace("provider-guardian-deadline-v2-"); const evidenceRoot = path.join(workspace, "provider-evidence"); const rawPath = path.join(evidenceRoot, "untrusted/vulnerability-report.json"); const nonce = Buffer.alloc(32, 0x58); const child = spawnGuardian(workspace, nonce); const completion = waitForChild(child); const readyPayload = readFrame(child.stdout!); child.stdin!.write(encodeProviderGuardianGuard({ kind: "vulnerability", nonce, deadlineEpochMs: Date.now() + 350, })); const ready = decodeProviderGuardianReady( await within(readyPayload, 1_000, "deadline guardian READY"), nonce, ); const sealedTempPath = path.join(evidenceRoot, ready.sealedTempLeaf); await expect(within(completion, 1_000, "guardian deadline")) .resolves.toEqual({ code: null, signal: "SIGKILL" }); await expect(lstat(rawPath)).rejects.toMatchObject({ code: "ENOENT" }); await expect(lstat(sealedTempPath)).rejects.toMatchObject({ code: "ENOENT" }); await expect(readdir(evidenceRoot)).resolves.toEqual(["untrusted"]); }); it("still publishes near the lease deadline when post-processing completes in time", async () => { const { startProviderGuardian } = await import( "../../scripts/lib/provider-guardian-client.ts" ); const workspace = await createWorkspace("provider-guardian-near-timeout-"); const lease = await startProviderGuardian({ kind: "vulnerability", workspaceRoot: workspace, leaseMs: 2_000, guardianScript: path.resolve("scripts/lib/provider-raw-guardian.ts"), }); await new Promise((resolve) => setTimeout(resolve, 1_000)); const bytes = Buffer.from('{"validated":"near-timeout"}\n'); await lease.publish(bytes); await lease.commit(); await expect(lstat(lease.rawPath)).rejects.toMatchObject({ code: "ENOENT" }); await expect(readFile(lease.sealedPath)).resolves.toEqual(bytes); }); it("signals guardian exit only while the provider scope latch is active", async () => { const lifecycle = await import("../../scripts/lib/provider-guardian-client.ts") as Record; expect(lifecycle.createProviderScopeGuardianLatch).toBeTypeOf("function"); const createLatch = lifecycle.createProviderScopeGuardianLatch as ( guardianExit: Promise, ) => Readonly<{ activeFailure: Promise; close(): Promise; failure(): Error | undefined; }>; let failAtBoundary!: (error: Error) => void; const boundaryExit = new Promise((resolve) => { failAtBoundary = resolve; }); const boundary = createLatch(boundaryExit); const boundaryError = new Error("guardian died at the collection boundary"); failAtBoundary(boundaryError); await boundary.close(); await expect(boundary.activeFailure).resolves.toBe(boundaryError); expect(boundary.failure()).toBe(boundaryError); let failActive!: (error: Error) => void; const activeExit = new Promise((resolve) => { failActive = resolve; }); const active = createLatch(activeExit); const activeError = new Error("guardian died while scope active"); failActive(activeError); await expect(active.activeFailure).resolves.toBe(activeError); expect(active.failure()).toBe(activeError); let failLate!: (error: Error) => void; const lateExit = new Promise((resolve) => { failLate = resolve; }); const late = createLatch(lateExit); await late.close(); const lateError = new Error("guardian died after scope collection"); failLate(lateError); await Promise.resolve(); expect(late.failure()).toBe(lateError); await expect(Promise.race([ late.activeFailure.then(() => "active"), new Promise((resolve) => setTimeout(() => resolve("inactive"), 25)), ])).resolves.toBe("inactive"); }); it("rejects a guardian lease whose derived sealed path differs from the configured target", async () => { const lifecycle = await import("../../scripts/lib/provider-guardian-client.ts") as Record; expect(lifecycle.assertProviderGuardianLeasePaths).toBeTypeOf("function"); const assertPaths = lifecycle.assertProviderGuardianLeasePaths as ( lease: Readonly<{ rawPath: string; sealedPath: string }>, expected: Readonly<{ rawPath: string; sealedPath: string }>, ) => void; const canonical = { rawPath: "/workspace/provider-evidence/untrusted/vulnerability-report.json", sealedPath: "/workspace/provider-evidence/vulnerability-report.json", }; expect(() => assertPaths(canonical, canonical)).not.toThrow(); expect(() => assertPaths(canonical, { ...canonical, sealedPath: "/workspace/provider-evidence/configured-alias.json", })).toThrow(/noncanonical sealed path/u); }); }); async function createWorkspace(prefix: string): Promise { const workspace = await mkdtemp(path.join(tmpdir(), prefix)); roots.push(workspace); await mkdir(path.join(workspace, "provider-evidence/untrusted"), { recursive: true }); return workspace; } async function createStalledGuardianFixture( workspace: string, ): Promise> { const guardianScript = path.join(workspace, "stalled-guardian.mjs"); const markerPath = path.join(workspace, "guardian-spawned"); await writeFile(guardianScript, [ 'import { writeFileSync } from "node:fs";', 'import path from "node:path";', 'writeFileSync(path.join(process.cwd(), "guardian-spawned"), String(process.pid));', "setInterval(() => undefined, 1_000);", "", ].join("\n")); return { guardianScript, markerPath }; } async function createPausedGuardianFixture(workspace: string): Promise { const guardianScript = path.join(workspace, "paused-guardian.mjs"); const realGuardian = pathToFileURL( path.resolve("scripts/lib/provider-raw-guardian.ts"), ).href; await writeFile(guardianScript, [ 'process.kill(process.pid, "SIGSTOP");', `await import(${JSON.stringify(realGuardian)});`, "", ].join("\n")); return guardianScript; } async function waitForFile(target: string): Promise { const deadline = Date.now() + 1_000; while (Date.now() <= deadline) { try { return await readFile(target); } catch (error) { if (!hasErrorCode(error, "ENOENT")) throw error; } await new Promise((resolve) => setTimeout(resolve, 5)); } throw new Error(`file did not appear: ${target}`); } type PublishedGuardian = Readonly<{ child: ReturnType; completion: Promise>; rawPath: string; sealedPath: string; sealedTempPath: string; }>; async function establishPublishedGuardian( workspace: string, nonce: Buffer, ): Promise { const { decodeProviderGuardianPublished, decodeProviderGuardianReady, encodeProviderGuardianGuard, encodeProviderGuardianPublish, } = await import("../../scripts/lib/provider-guardian-protocol.ts"); const evidenceRoot = path.join(workspace, "provider-evidence"); const rawPath = path.join(evidenceRoot, "untrusted/vulnerability-report.json"); const sealedPath = path.join(evidenceRoot, "vulnerability-report.json"); const child = spawnGuardian(workspace, nonce); const completion = waitForChild(child); const readyPayload = readFrame(child.stdout!); child.stdin!.write(encodeProviderGuardianGuard({ kind: "vulnerability", nonce, deadlineEpochMs: Date.now() + 3_000, })); const ready = decodeProviderGuardianReady( await within(readyPayload, 1_000, "guardian READY"), nonce, ); const sealedTempPath = path.join(evidenceRoot, ready.sealedTempLeaf); const bytes = Buffer.from('{"validated":"pending-commit"}\n'); const handle = await open(sealedTempPath, constants.O_WRONLY | constants.O_NOFOLLOW); try { await handle.writeFile(bytes); await handle.chmod(0o400); await handle.sync(); } finally { await handle.close(); } const publishedPayload = readFrame(child.stdout!); child.stdin!.write(encodeProviderGuardianPublish({ nonce, sealedDev: ready.sealedDev, sealedIno: ready.sealedIno, size: bytes.byteLength, sha256: createHash("sha256").update(bytes).digest("hex"), })); decodeProviderGuardianPublished( await within(publishedPayload, 1_000, "guardian PUBLISHED"), nonce, { dev: ready.sealedDev, ino: ready.sealedIno }, ); return { child, completion, rawPath, sealedPath, sealedTempPath }; } async function assertTransactionAbsent(transaction: PublishedGuardian): Promise { await expect(lstat(transaction.rawPath)).rejects.toMatchObject({ code: "ENOENT" }); await expect(lstat(transaction.sealedPath)).rejects.toMatchObject({ code: "ENOENT" }); await expect(lstat(transaction.sealedTempPath)).rejects.toMatchObject({ code: "ENOENT" }); } async function readFrame(stream: NodeJS.ReadableStream): Promise { return await new Promise((resolve, reject) => { let pending = Buffer.alloc(0); const onData = (chunk: Buffer | string): void => { pending = Buffer.concat([pending, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]); if (pending.byteLength < 4) return; const payloadBytes = pending.readUInt32BE(0); if (pending.byteLength < payloadBytes + 4) return; cleanup(); if (pending.byteLength !== payloadBytes + 4) { reject(new Error("guardian acknowledgement contained trailing bytes")); return; } resolve(pending.subarray(4)); }; const onEnd = (): void => { cleanup(); reject(new Error("guardian closed before acknowledgement")); }; const cleanup = (): void => { stream.removeListener("data", onData); stream.removeListener("end", onEnd); }; stream.on("data", onData); stream.once("end", onEnd); }); } async function waitForChild( child: ReturnType, ): Promise> { return await new Promise((resolve, reject) => { child.once("error", reject); child.once("close", (code, signal) => resolve({ code, signal })); }); } function spawnGuardian( workspace: string, nonce: Buffer = Buffer.alloc(32, 0x7d), ): ReturnType { const evidenceRoot = path.join(workspace, "provider-evidence"); const rawDirectory = path.join(evidenceRoot, "untrusted"); const noncePrefix = nonce.subarray(0, 16).toString("hex"); const openedFds: number[] = []; try { openedFds.push(openSync( rawDirectory, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW, )); openedFds.push(openSync( evidenceRoot, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW, )); openedFds.push(openSync( path.join( rawDirectory, `.vulnerability-report.json.guardian-${noncePrefix}.raw.tmp`, ), constants.O_RDWR | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600, )); openedFds.push(openSync( path.join( evidenceRoot, `.vulnerability-report.json.guardian-${noncePrefix}.tmp`, ), constants.O_RDWR | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600, )); return spawn(process.execPath, [path.resolve("scripts/lib/provider-raw-guardian.ts")], { cwd: workspace, env: {}, stdio: ["pipe", "pipe", "pipe", ...openedFds], }); } finally { for (const fd of openedFds) closeSync(fd); } } function spawnGuardianWithPrivateModes( workspace: string, nonce: Buffer, modes: Readonly<{ rawMode: number; sealedMode: number }>, ): Readonly<{ child: ReturnType; rawStagingPath: string; sealedTempPath: string; }> { const evidenceRoot = path.join(workspace, "provider-evidence"); const rawDirectory = path.join(evidenceRoot, "untrusted"); const noncePrefix = nonce.subarray(0, 16).toString("hex"); const rawStagingPath = path.join( rawDirectory, `.vulnerability-report.json.guardian-${noncePrefix}.raw.tmp`, ); const sealedTempPath = path.join( evidenceRoot, `.vulnerability-report.json.guardian-${noncePrefix}.tmp`, ); const openedFds: number[] = []; try { openedFds.push(openSync( rawDirectory, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW, )); openedFds.push(openSync( evidenceRoot, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW, )); const rawStagingFd = openSync( rawStagingPath, constants.O_RDWR | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600, ); openedFds.push(rawStagingFd); fchmodSync(rawStagingFd, modes.rawMode); const sealedTempFd = openSync( sealedTempPath, constants.O_RDWR | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600, ); openedFds.push(sealedTempFd); fchmodSync(sealedTempFd, modes.sealedMode); const child = spawn(process.execPath, [path.resolve("scripts/lib/provider-raw-guardian.ts")], { cwd: workspace, env: {}, stdio: ["pipe", "pipe", "pipe", ...openedFds], }); return { child, rawStagingPath, sealedTempPath }; } finally { for (const fd of openedFds) closeSync(fd); } } async function waitForDirectoryEntry( directory: string, expectedLeaf: string, onEntry: () => void, ): Promise { await new Promise((resolve, reject) => { const watcher = watch(directory, (_event, filename) => { if (filename?.toString() !== expectedLeaf) return; try { onEntry(); resolve(); } catch (error) { reject(error); } finally { watcher.close(); } }); watcher.once("error", (error) => { watcher.close(); reject(error); }); }); } async function within(operation: Promise, timeoutMs: number, label: string): Promise { let timer: NodeJS.Timeout | undefined; try { return await Promise.race([ operation, new Promise((_resolve, reject) => { timer = setTimeout(() => reject(new Error(`${label} timed out`)), timeoutMs); }), ]); } finally { if (timer) clearTimeout(timer); } } async function directChildPids(parentPid: number): Promise> { const value = await readFile(`/proc/${parentPid}/task/${parentPid}/children`, "utf8"); return new Set(value.trim().split(/\s+/u).filter(Boolean).map(Number)); } async function waitForNewDirectChild(previous: ReadonlySet): Promise { const deadline = Date.now() + 1_000; while (Date.now() <= deadline) { for (const pid of await directChildPids(process.pid)) { if (!previous.has(pid)) return pid; } await new Promise((resolve) => setTimeout(resolve, 5)); } throw new Error("provider guardian child did not appear"); } async function waitForStoppedProcess(pid: number): Promise { const deadline = Date.now() + 1_000; while (Date.now() <= deadline) { const status = await readFile(`/proc/${pid}/status`, "utf8"); if (/^State:\s+T/mu.test(status)) return; await new Promise((resolve) => setTimeout(resolve, 5)); } throw new Error("provider guardian child did not stop before bootstrap"); } function hasErrorCode(error: unknown, code: string): boolean { return Boolean(error && typeof error === "object" && "code" in error && error.code === code); }