229 lines
11 KiB
JavaScript
229 lines
11 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { execFile } from "node:child_process";
|
|
import {
|
|
chmod, lstat, mkdir, mkdtemp, readFile, realpath, rm, symlink, writeFile,
|
|
} from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { dirname, join } from "node:path";
|
|
import { promisify } from "node:util";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
canonicalIntegrationBase,
|
|
cleanupOwnedRun,
|
|
createOwnedRun,
|
|
deriveOverall,
|
|
executeChecks,
|
|
readAndValidateOwnership,
|
|
runCommand,
|
|
runIntegration,
|
|
scalarSecretBytes,
|
|
scanSecrets,
|
|
validateReport,
|
|
validateRunRoot,
|
|
} from "./p1-acceptance.mjs";
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
const roots = [];
|
|
async function fakeRepository() {
|
|
const root = await mkdtemp(join(tmpdir(), "p1 acceptance repository with spaces-"));
|
|
roots.push(root);
|
|
await mkdir(join(root, ".artifacts", "p1-integration"), { recursive: true });
|
|
return await realpath(root);
|
|
}
|
|
|
|
test.afterEach(async () => {
|
|
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
|
});
|
|
|
|
test("run roots are only canonical direct integration children", async () => {
|
|
const repositoryRoot = await fakeRepository();
|
|
const base = canonicalIntegrationBase(repositoryRoot);
|
|
const id = `p1-${"a".repeat(32)}`;
|
|
assert.equal(validateRunRoot(repositoryRoot, join(base, id), id), join(base, id));
|
|
for (const candidate of [
|
|
base,
|
|
join(repositoryRoot, ".artifacts", "manual-acceptance", id),
|
|
join(base, id, "nested"),
|
|
join(base, "foreign"),
|
|
join(dirname(base), id),
|
|
]) assert.throws(() => validateRunRoot(repositoryRoot, candidate, id));
|
|
assert.throws(() => validateRunRoot(repositoryRoot, join(base, `p1-${"A".repeat(32)}`), `p1-${"A".repeat(32)}`));
|
|
});
|
|
|
|
test("cleanup refuses every unowned or ambiguous root", async () => {
|
|
const repositoryRoot = await fakeRepository();
|
|
const base = canonicalIntegrationBase(repositoryRoot);
|
|
const cases = [
|
|
["missing ownership", async (run) => rm(join(run.root, "ownership.json"))],
|
|
["malformed ownership", async (run) => writeFile(join(run.root, "ownership.json"), "{")],
|
|
["mismatched root", async (run) => {
|
|
const value = JSON.parse(await readFile(join(run.root, "ownership.json"), "utf8"));
|
|
value.root = join(base, `p1-${"b".repeat(32)}`);
|
|
await writeFile(join(run.root, "ownership.json"), JSON.stringify(value));
|
|
}],
|
|
["mismatched pid", async (run) => {
|
|
const value = JSON.parse(await readFile(join(run.root, "ownership.json"), "utf8"));
|
|
value.pid += 1;
|
|
await writeFile(join(run.root, "ownership.json"), JSON.stringify(value));
|
|
}],
|
|
["wrong resource list", async (run) => {
|
|
const value = JSON.parse(await readFile(join(run.root, "ownership.json"), "utf8"));
|
|
value.resources.push(join(repositoryRoot, "foreign"));
|
|
await writeFile(join(run.root, "ownership.json"), JSON.stringify(value));
|
|
}],
|
|
];
|
|
for (const [, mutate] of cases) {
|
|
const run = await createOwnedRun({ repositoryRoot });
|
|
await mutate(run);
|
|
await assert.rejects(cleanupOwnedRun({ repositoryRoot, runRoot: run.root, expectedNonce: run.nonce }));
|
|
assert.equal((await lstat(run.root)).isDirectory(), true);
|
|
}
|
|
const wrongNonce = await createOwnedRun({ repositoryRoot });
|
|
await assert.rejects(cleanupOwnedRun({ repositoryRoot, runRoot: wrongNonce.root, expectedNonce: "0".repeat(64) }));
|
|
const symlinkRun = await createOwnedRun({ repositoryRoot });
|
|
const target = `${symlinkRun.root}-target`;
|
|
await rm(symlinkRun.root, { recursive: true });
|
|
await mkdir(target);
|
|
await symlink(target, symlinkRun.root);
|
|
await assert.rejects(cleanupOwnedRun({ repositoryRoot, runRoot: symlinkRun.root, expectedNonce: symlinkRun.nonce }));
|
|
for (const bad of [base, join(repositoryRoot, ".artifacts", "manual-acceptance"), join(base, "foreign")]) {
|
|
await assert.rejects(cleanupOwnedRun({ repositoryRoot, runRoot: bad, expectedNonce: "0".repeat(64) }));
|
|
}
|
|
});
|
|
|
|
test("cleanup atomically removes one owned root and preserves siblings", async () => {
|
|
const repositoryRoot = await fakeRepository();
|
|
const run = await createOwnedRun({ repositoryRoot });
|
|
const sibling = join(canonicalIntegrationBase(repositoryRoot), `p1-${"c".repeat(32)}`);
|
|
await mkdir(sibling);
|
|
await writeFile(join(sibling, "sentinel"), "foreign");
|
|
await readAndValidateOwnership({ repositoryRoot, runRoot: run.root, expectedNonce: run.nonce });
|
|
await cleanupOwnedRun({ repositoryRoot, runRoot: run.root, expectedNonce: run.nonce });
|
|
await assert.rejects(lstat(run.root));
|
|
assert.equal(await readFile(join(sibling, "sentinel"), "utf8"), "foreign");
|
|
});
|
|
|
|
function validReport(checks = [{
|
|
id: "preflight", status: "PASS", startedAt: "2026-08-09T00:00:00.000Z",
|
|
finishedAt: "2026-08-09T00:00:01.000Z", commands: ["git"],
|
|
artifacts: [{ path: "logs/preflight.json", sha256: "a".repeat(64) }],
|
|
}]) {
|
|
return {
|
|
schemaVersion: 1, runId: `p1-${"d".repeat(32)}`, startedAt: "2026-08-09T00:00:00.000Z",
|
|
finishedAt: "2026-08-09T00:00:02.000Z", command: "p1-acceptance integration --keep",
|
|
overall: deriveOverall(checks), checks,
|
|
};
|
|
}
|
|
|
|
test("report validation enforces uniqueness, derivation, safe evidence, hashes, times, and commands", () => {
|
|
assert.doesNotThrow(() => validateReport(validReport()));
|
|
const mutations = [
|
|
(r) => r.checks.push(structuredClone(r.checks[0])),
|
|
(r) => { r.checks[0].attempt = 1; },
|
|
(r) => { r.checks[0].artifacts[0].path = "../secret"; },
|
|
(r) => { r.checks[0].artifacts[0].sha256 = "bad"; },
|
|
(r) => { r.checks[0].startedAt = "today"; },
|
|
(r) => { r.checks[0].commands = ["git status"]; },
|
|
(r) => { r.overall = "PASS"; r.checks[0].status = "FAIL"; },
|
|
(r) => { r.nested = { retries: 2 }; },
|
|
];
|
|
for (const mutate of mutations) {
|
|
const report = validReport(); mutate(report); assert.throws(() => validateReport(report));
|
|
}
|
|
});
|
|
|
|
test("injected failure executes once, retains diagnostics, and returns nonzero", async () => {
|
|
const repositoryRoot = await fakeRepository();
|
|
let calls = 0;
|
|
const result = await runIntegration({
|
|
repositoryRoot, keep: false, failAt: "sample",
|
|
checks: [{ id: "sample", run: async () => { calls += 1; return { commands: [], artifacts: [] }; } }],
|
|
});
|
|
assert.equal(result.exitCode, 1);
|
|
assert.equal(calls, 1);
|
|
assert.equal((await lstat(result.runRoot)).isDirectory(), true);
|
|
const report = JSON.parse(await readFile(join(result.runRoot, "report.json"), "utf8"));
|
|
assert.equal(report.checks.filter((check) => check.status === "FAIL").length, 1);
|
|
assert.equal(report.checks[0].id, "sample");
|
|
});
|
|
|
|
test("executeChecks never repeats a scenario", async () => {
|
|
const calls = new Map();
|
|
const result = await executeChecks({
|
|
checks: ["one", "two"].map((id) => ({ id, run: async () => { calls.set(id, (calls.get(id) ?? 0) + 1); return {}; } })),
|
|
failAt: "two",
|
|
});
|
|
assert.equal(result.length, 2);
|
|
assert.deepEqual(Object.fromEntries(calls), { one: 1, two: 1 });
|
|
assert.equal(result[1].status, "FAIL");
|
|
});
|
|
|
|
test("scalar fixture secret files contain no harness-invalid whitespace", () => {
|
|
const bytes = scalarSecretBytes("CANARY-secret-value-123456");
|
|
assert.equal(bytes.toString("utf8"), "CANARY-secret-value-123456");
|
|
assert.equal([...bytes].some((byte) => /\s/.test(String.fromCharCode(byte))), false);
|
|
assert.throws(() => scalarSecretBytes("bad secret"));
|
|
});
|
|
|
|
test("secret scanner excludes only the direct fixture-secrets subtree", async () => {
|
|
const repositoryRoot = await fakeRepository();
|
|
const run = await createOwnedRun({ repositoryRoot });
|
|
const canary = "CANARY-secret-value-123456";
|
|
await mkdir(join(run.root, "fixture-secrets"));
|
|
await writeFile(join(run.root, "fixture-secrets", "allowed"), canary);
|
|
const paths = [
|
|
"logs/a.log", "responses/a.json", "rendered/a.yaml", "exports/raw/a.zip",
|
|
"exports/extracted/a.md", "requests/a.json", "report-preview.md", "nested/fixture-secrets/not-excluded",
|
|
];
|
|
for (const path of paths) {
|
|
await mkdir(dirname(join(run.root, path)), { recursive: true });
|
|
await writeFile(join(run.root, path), `prefix ${canary} suffix`);
|
|
}
|
|
const findings = await scanSecrets({ runRoot: run.root, forbiddenValues: [canary] });
|
|
assert.deepEqual(new Set(findings.map((finding) => finding.path)), new Set(paths));
|
|
});
|
|
|
|
test("secret scanner examines reachable Git blobs, not just loose file bytes", async () => {
|
|
const repositoryRoot = await fakeRepository();
|
|
const run = await createOwnedRun({ repositoryRoot });
|
|
const canary = "GIT-CANARY-secret-value-987654";
|
|
const gitRoot = join(run.root, "author");
|
|
await mkdir(gitRoot);
|
|
await execFileAsync("git", ["init", "--initial-branch=main"], { cwd: gitRoot });
|
|
await execFileAsync("git", ["config", "user.name", "Scanner Test"], { cwd: gitRoot });
|
|
await execFileAsync("git", ["config", "user.email", "scanner@example.invalid"], { cwd: gitRoot });
|
|
await writeFile(join(gitRoot, "secret.txt"), canary);
|
|
await execFileAsync("git", ["add", "secret.txt"], { cwd: gitRoot });
|
|
await execFileAsync("git", ["commit", "-m", "secret blob"], { cwd: gitRoot });
|
|
await execFileAsync("git", ["rm", "secret.txt"], { cwd: gitRoot });
|
|
await execFileAsync("git", ["commit", "-m", "remove worktree copy"], { cwd: gitRoot });
|
|
const findings = await scanSecrets({ runRoot: run.root, forbiddenValues: [canary] });
|
|
assert.equal(findings.some((finding) => finding.path.startsWith("git-object:")), true);
|
|
});
|
|
|
|
test("successful lifecycle honors keep and cleanup", async () => {
|
|
const repositoryRoot = await fakeRepository();
|
|
const check = [{ id: "sample", run: async () => ({ commands: [], artifacts: [] }) }];
|
|
const kept = await runIntegration({ repositoryRoot, keep: true, checks: check });
|
|
assert.equal(kept.exitCode, 0);
|
|
assert.equal((await lstat(kept.runRoot)).isDirectory(), true);
|
|
const cleaned = await runIntegration({ repositoryRoot, keep: false, checks: check });
|
|
assert.equal(cleaned.exitCode, 0);
|
|
await assert.rejects(lstat(cleaned.runRoot));
|
|
});
|
|
|
|
test("command helper accepts only executable plus separate argv", async () => {
|
|
await assert.rejects(runCommand("git status"));
|
|
await assert.rejects(runCommand({ executable: "/bin/echo", argv: "hello" }));
|
|
await assert.rejects(runCommand({ executable: "/bin/echo", argv: [], shell: true }));
|
|
await assert.rejects(runCommand({ executable: "git status; rm -rf /", argv: [] }));
|
|
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);
|
|
const result = await runCommand({ executable, argv: ["literal;not-a-shell"] });
|
|
assert.equal(result.stdout, "literal;not-a-shell");
|
|
assert.equal(result.code, 0);
|
|
});
|