fix: close P1 acceptance audit gaps
This commit is contained in:
@@ -8,6 +8,8 @@ import { dirname, join } from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { createServer, connect } from "node:net";
|
||||
import dgram from "node:dgram";
|
||||
import { Worker } from "node:worker_threads";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
@@ -16,6 +18,8 @@ import {
|
||||
buildSafeEnvironment,
|
||||
installExternalFetchGuard,
|
||||
installNetworkGuard,
|
||||
installProductionSurfaceGuard,
|
||||
resolveProductionExecutables,
|
||||
negativeRequestEvidence,
|
||||
cleanupOwnedRun,
|
||||
createOwnedRun,
|
||||
@@ -275,13 +279,17 @@ test("command helper accepts only executable plus separate argv", async () => {
|
||||
await assert.rejects(runCommand({ executable: "/bin/echo", argv: [], shell: true }));
|
||||
await assert.rejects(runCommand({ executable: "git status; rm -rf /", argv: [] }));
|
||||
await assert.rejects(runCommand({ executable: "/tmp/git", argv: ["--version"] }), /command executable is not allowlisted/);
|
||||
await assert.rejects(runCommand({ executable: "tht", argv: ["config", "check"] }), /command executable is not allowlisted/);
|
||||
await assert.rejects(runCommand({ executable: "tht", argv: ["config", "check"] }), /command executable is invalid/);
|
||||
const repositoryRoot = await fakeRepository();
|
||||
const executable = join(repositoryRoot, "executable with spaces");
|
||||
await writeFile(executable, "#!/bin/sh\nprintf '%s' \"$1\"\n", { mode: 0o700 });
|
||||
await chmod(executable, 0o700);
|
||||
await assert.rejects(runCommand({ executable, argv: ["literal;not-a-shell"] }), /command executable is not allowlisted/);
|
||||
const result = await runCommand({ executable: "git", argv: ["--version"] });
|
||||
const tht = join(repositoryRoot, "harness", ".venv", "bin", "tht");
|
||||
await mkdir(dirname(tht), { recursive: true });
|
||||
await writeFile(tht, "#!/bin/sh\nexit 0\n", { mode: 0o700 });
|
||||
const { gitPath } = await resolveProductionExecutables({ repositoryRoot, thtBin: tht, ambientPath: process.env.PATH });
|
||||
const result = await runCommand({ executable: gitPath, argv: ["--version"] });
|
||||
assert.match(result.stdout, /^git version /);
|
||||
assert.equal(result.code, 0);
|
||||
});
|
||||
@@ -293,8 +301,7 @@ test("safe environment rejects ambient THT and keeps only strict process allowli
|
||||
fixture: { THT_BIN: "/fixture/tht", THT_WORKSPACE_SECRET_ROOTS: "/fixture/secrets" },
|
||||
});
|
||||
assert.deepEqual(safe, {
|
||||
PATH: "/safe/bin", HOME: "/home/test", LANG: "C",
|
||||
THT_BIN: "/fixture/tht", THT_WORKSPACE_SECRET_ROOTS: "/fixture/secrets",
|
||||
LANG: "C", THT_BIN: "/fixture/tht", THT_WORKSPACE_SECRET_ROOTS: "/fixture/secrets",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -434,3 +441,151 @@ test("secret scan fails closed when either expected Git repository is missing",
|
||||
await assert.rejects(scanSecrets({ runRoot: run.root, forbiddenValues: ["CANARY-value-123"] }), new RegExp(`missing expected Git repository: ${missing.replace(".", "\\.")}`));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
test("runIntegration fails closed when a later duplicate overwrites stale artifact evidence", async () => {
|
||||
const repositoryRoot = await fakeRepository();
|
||||
const checks = exactScenarios(async (id) => {
|
||||
if (id === CHECK_IDS[0]) {
|
||||
await mkdir(join(repositoryRoot, ".artifacts", "p1-integration", "scratch"), { recursive: true });
|
||||
}
|
||||
return { commands: [], artifacts: [] };
|
||||
});
|
||||
const result = await runIntegration({
|
||||
repositoryRoot, keep: true,
|
||||
setup: async (run, _repositoryRoot, _env, ctx) => {
|
||||
const path = join(run.root, "logs", "overwritten.json");
|
||||
await mkdir(dirname(path), { recursive: true });
|
||||
await writeFile(path, "first");
|
||||
const stale = { path: "logs/overwritten.json", sha256: "a7937b64b8caa58f03721bb6bacf9e92a2c78987f5d1692a065a4698e006c4ca" };
|
||||
checks[0].run = async () => ({ commands: [], artifacts: [stale] });
|
||||
checks[1].run = async () => {
|
||||
await writeFile(path, "second");
|
||||
return { commands: [], artifacts: [{ path: stale.path, sha256: "16367aacb67a4a017c8da8ab95682ccb389c61bb315f3425e2f2666f2476d1ce" }] };
|
||||
};
|
||||
return ctx;
|
||||
},
|
||||
checks,
|
||||
});
|
||||
assert.equal(result.exitCode, 1);
|
||||
assert.equal(result.report.checks.length, 15);
|
||||
assert(result.report.checks.every(({ status, artifacts }) => status === "FAIL" && artifacts.length === 0));
|
||||
});
|
||||
|
||||
test("production surface guard rejects and records UDP, Worker, git ls-remote, and unexpected python", async () => {
|
||||
const repositoryRoot = await fakeRepository();
|
||||
const thtPath = join(repositoryRoot, "harness", ".venv", "bin", "tht");
|
||||
await mkdir(dirname(thtPath), { recursive: true });
|
||||
await writeFile(thtPath, "#!/bin/sh\nexit 0\n", { mode: 0o700 });
|
||||
await chmod(thtPath, 0o700);
|
||||
const executables = await resolveProductionExecutables({ repositoryRoot, thtBin: thtPath, ambientPath: process.env.PATH });
|
||||
const guard = installProductionSurfaceGuard({
|
||||
...executables, runRoot: repositoryRoot, environment: { ...process.env }, originalFetch: globalThis.fetch,
|
||||
});
|
||||
try {
|
||||
assert.throws(() => dgram.createSocket("udp4"), /prohibited production surface/);
|
||||
assert.throws(() => new Worker("", { eval: true }), /prohibited production surface/);
|
||||
await assert.rejects(runCommand({ executable: executables.gitPath, argv: ["ls-remote", "https://example.com/repo.git"] }), /Git command is prohibited/);
|
||||
const childProcess = await import("node:child_process");
|
||||
assert.throws(() => childProcess.spawn(executables.pythonPath, ["-c", "print('unexpected')"]), /child command is prohibited/);
|
||||
assert.deepEqual(new Set(guard.events.filter(({ outcome }) => outcome === "REJECTED").map(({ surface }) => surface)),
|
||||
new Set(["dgram", "worker_threads", "child_process"]));
|
||||
} finally {
|
||||
guard.restore();
|
||||
}
|
||||
});
|
||||
|
||||
test("listener close rejection retains listening truth and forces exact-15 FAIL", async () => {
|
||||
const repositoryRoot = await fakeRepository();
|
||||
const server = createServer();
|
||||
await new Promise((resolvePromise, reject) => server.listen(0, "127.0.0.1", (error) => error ? reject(error) : resolvePromise()));
|
||||
const address = server.address();
|
||||
assert(address && typeof address === "object");
|
||||
const result = await runIntegration({
|
||||
repositoryRoot, keep: false, checks: exactScenarios(),
|
||||
setup: async (run, _repositoryRoot, _env, ctx) => {
|
||||
ctx.services = [{ name: "primary", baseUrl: `http://127.0.0.1:${address.port}`, app: { close: async () => { throw new Error("close rejected"); } } }];
|
||||
const value = JSON.parse(await readFile(join(run.root, "ownership.json"), "utf8"));
|
||||
value.listeners[0] = { name: "primary", kind: "fastify", host: "127.0.0.1", requestedPort: 0, actualPort: address.port, pid: process.pid, state: "listening" };
|
||||
await writeFile(join(run.root, "ownership.json"), `${JSON.stringify(value, null, 2)}\n`);
|
||||
return ctx;
|
||||
},
|
||||
});
|
||||
assert.equal(result.exitCode, 1);
|
||||
assert.equal(result.retained, true);
|
||||
const owner = JSON.parse(await readFile(join(result.runRoot, "ownership.json"), "utf8"));
|
||||
assert.notEqual(owner.listeners[0].state, "closed");
|
||||
assert(result.report.checks.every(({ status }) => status === "FAIL"));
|
||||
await new Promise((resolvePromise) => server.close(resolvePromise));
|
||||
});
|
||||
|
||||
test("ownership close write failure forces retained exact-15 FAIL", async () => {
|
||||
const repositoryRoot = await fakeRepository();
|
||||
const server = createServer();
|
||||
await new Promise((resolvePromise, reject) => server.listen(0, "127.0.0.1", (error) => error ? reject(error) : resolvePromise()));
|
||||
const address = server.address();
|
||||
assert(address && typeof address === "object");
|
||||
const result = await runIntegration({
|
||||
repositoryRoot, keep: false, checks: exactScenarios(),
|
||||
ownershipWriter: async (_run, update) => { if (update?.state === "closed") throw new Error("owned write rejected"); },
|
||||
setup: async (_run, _repositoryRoot, _env, ctx) => {
|
||||
ctx.services = [{ name: "primary", baseUrl: `http://127.0.0.1:${address.port}`, app: { close: async () => await new Promise((resolvePromise) => server.close(resolvePromise)) } }];
|
||||
return ctx;
|
||||
},
|
||||
});
|
||||
assert.equal(result.exitCode, 1);
|
||||
assert.equal(result.retained, true);
|
||||
assert(result.report.checks.every(({ status }) => status === "FAIL"));
|
||||
});
|
||||
|
||||
test("nested runIntegration is rejected before process-global mutation and outer restoration remains owned", async () => {
|
||||
const repositoryRoot = await fakeRepository();
|
||||
const originalFetch = globalThis.fetch;
|
||||
const originalPath = process.env.PATH;
|
||||
let nestedError;
|
||||
const result = await runIntegration({
|
||||
repositoryRoot, keep: true, checks: exactScenarios(),
|
||||
setup: async (_run, _repositoryRoot, _env, ctx) => {
|
||||
try { await runIntegration({ repositoryRoot, keep: true, checks: exactScenarios() }); } catch (error) { nestedError = error; }
|
||||
assert.equal(globalThis.fetch, originalFetch);
|
||||
assert.equal(process.env.PATH, originalPath);
|
||||
return ctx;
|
||||
},
|
||||
});
|
||||
assert.match(nestedError?.message ?? "", /already active/);
|
||||
assert.equal(result.exitCode, 0);
|
||||
assert.equal(globalThis.fetch, originalFetch);
|
||||
assert.equal(process.env.PATH, originalPath);
|
||||
});
|
||||
|
||||
|
||||
test("environment tampering fails the audit and restores the caller environment", async () => {
|
||||
const repositoryRoot = await fakeRepository();
|
||||
const before = { ...process.env };
|
||||
const checks = exactScenarios(async (id) => {
|
||||
if (id === CHECK_IDS[0]) process.env.P1_ACCEPTANCE_UNOWNED = "tampered";
|
||||
return { commands: [], artifacts: [] };
|
||||
});
|
||||
const result = await runIntegration({
|
||||
repositoryRoot, keep: true, checks,
|
||||
setup: async (_run, _repositoryRoot, _env, ctx) => { ctx.env = { P1_ACCEPTANCE_OWNED: "yes" }; return ctx; },
|
||||
});
|
||||
assert.equal(result.exitCode, 1);
|
||||
assert(result.report.checks.every(({ status }) => status === "FAIL"));
|
||||
assert.deepEqual({ ...process.env }, before);
|
||||
});
|
||||
|
||||
test("production guard detects global tampering and restores without stranding patches", async () => {
|
||||
const repositoryRoot = await fakeRepository();
|
||||
const thtPath = join(repositoryRoot, "harness", ".venv", "bin", "tht");
|
||||
await mkdir(dirname(thtPath), { recursive: true });
|
||||
await writeFile(thtPath, "#!/bin/sh\nexit 0\n", { mode: 0o700 });
|
||||
const executables = await resolveProductionExecutables({ repositoryRoot, thtBin: thtPath });
|
||||
const originalFetch = globalThis.fetch;
|
||||
const guard = installProductionSurfaceGuard({ ...executables, runRoot: repositoryRoot, environment: { ...process.env }, originalFetch });
|
||||
globalThis.fetch = originalFetch;
|
||||
assert.throws(() => guard.restore(), /ownership restoration failed/);
|
||||
assert.equal(globalThis.fetch, originalFetch);
|
||||
const childProcess = await import("node:child_process");
|
||||
assert.doesNotThrow(() => childProcess.spawn);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user