fix(pi-workflow): bound host command execution
This commit is contained in:
@@ -16,6 +16,7 @@ import { promisify } from "node:util";
|
||||
import { deflateSync } from "node:zlib";
|
||||
import {
|
||||
ApprovedHostCommandExecutor,
|
||||
ExecFileCommandRunner,
|
||||
FilesystemPrototypeValidator,
|
||||
GitStateProbe,
|
||||
GitWorkspaceManager,
|
||||
@@ -283,6 +284,27 @@ test("Git probe and approved host commands enforce identity and ownership", asyn
|
||||
'process.exit(process.env.PI_OFFLINE === "1" ? 0 : 1)',
|
||||
"utf8",
|
||||
);
|
||||
await writeFile(
|
||||
path.join(directory, "spawn-hanging-child.cjs"),
|
||||
[
|
||||
'const { spawn } = require("node:child_process");',
|
||||
'const { writeFileSync } = require("node:fs");',
|
||||
'const child = spawn(process.execPath, ["delayed-write.cjs"], { stdio: "ignore" });',
|
||||
'writeFileSync("src/timeout-pids.json", JSON.stringify([process.pid, child.pid]));',
|
||||
"setInterval(() => {}, 1_000);",
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
);
|
||||
await writeFile(
|
||||
path.join(directory, "delayed-write.cjs"),
|
||||
[
|
||||
'const { writeFileSync } = require("node:fs");',
|
||||
'process.on("SIGTERM", () => {});',
|
||||
'setTimeout(() => writeFileSync("src/delayed.txt", "late\\n"), 1_200);',
|
||||
"setInterval(() => {}, 1_000);",
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
);
|
||||
await execFileAsync("git", ["-C", directory, "add", "."]);
|
||||
await execFileAsync("git", ["-C", directory, "commit", "-qm", "initial"]);
|
||||
const probe = new GitStateProbe();
|
||||
@@ -315,6 +337,35 @@ test("Git probe and approved host commands enforce identity and ownership", asyn
|
||||
args: ["PI_OFFLINE=1", process.execPath, "check-offline.cjs"],
|
||||
});
|
||||
assert.equal(offline.result.code, 0);
|
||||
const boundedExecutor = new ApprovedHostCommandExecutor(
|
||||
new ExecFileCommandRunner(),
|
||||
undefined,
|
||||
250,
|
||||
);
|
||||
const startedAt = Date.now();
|
||||
const timedOut = await boundedExecutor.run({
|
||||
worktreeRoot: directory,
|
||||
ownedPaths: ["src"],
|
||||
executable: process.execPath,
|
||||
args: ["spawn-hanging-child.cjs"],
|
||||
});
|
||||
assert.equal(timedOut.result.code, 124);
|
||||
assert.match(timedOut.result.stderr, /timed out after 250ms/);
|
||||
assert.ok(Date.now() - startedAt < 2_500);
|
||||
const timedOutProcessIds = JSON.parse(
|
||||
await readFile(path.join(directory, "src", "timeout-pids.json"), "utf8"),
|
||||
) as number[];
|
||||
for (const processId of timedOutProcessIds) {
|
||||
assert.throws(
|
||||
() => process.kill(processId, 0),
|
||||
(error: unknown) => (error as NodeJS.ErrnoException).code === "ESRCH",
|
||||
);
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 1_000));
|
||||
await assert.rejects(
|
||||
access(path.join(directory, "src", "delayed.txt")),
|
||||
/ENOENT/,
|
||||
);
|
||||
await assert.rejects(
|
||||
executor.run({
|
||||
worktreeRoot: directory,
|
||||
|
||||
Reference in New Issue
Block a user