fix: seal P1 acceptance process boundaries

This commit is contained in:
2026-08-09 22:51:56 +02:00
parent a3129b8b81
commit 7d8fb065b5
4 changed files with 480 additions and 142 deletions
+281 -109
View File
@@ -23,6 +23,7 @@ const mutableWorkerThreads = require("node:worker_threads");
let commandEventSink;
let activeCommandCheckId;
let activeExecutablePolicy;
let activePolicyRejectionSink;
let integrationOwner;
let productionSurfaceOwner;
const RUN_ID = /^p1-[0-9a-f]{32}$/;
@@ -227,41 +228,142 @@ function resolveTrustedSystemExecutableSync(name) {
throw new Error(`cannot resolve trusted system executable: ${name}`);
}
export async function resolveProductionExecutables({ repositoryRoot, thtBin } = {}) {
export async function resolveProductionExecutables({ repositoryRoot } = {}) {
const repo = canonicalRoot(repositoryRoot);
const gitPath = resolveTrustedSystemExecutableSync("git");
const pythonPath = resolveTrustedSystemExecutableSync("python3");
const expectedThtRoot = join(repo, "harness", ".venv");
const candidateTht = thtBin ?? join(expectedThtRoot, "bin", "tht");
if (!isAbsolute(candidateTht)) throw new Error("THT executable must be absolute");
const thtPath = realpathSync(candidateTht);
const thtRelative = relative(expectedThtRoot, thtPath);
if (thtRelative.startsWith("..") || isAbsolute(thtRelative)) throw new Error("THT executable leaves the repository virtual environment");
await access(thtPath, fsConstants.X_OK);
return { gitPath, pythonPath, thtPath };
const thtPath = join(repo, "harness", ".venv", "bin", "tht");
let entry;
try { entry = lstatSync(thtPath); } catch { throw new Error("trusted THT executable is unavailable"); }
if (!entry.isFile() || entry.isSymbolicLink() || realpathSync(thtPath) !== thtPath) throw new Error("trusted THT entrypoint identity is invalid");
accessSync(thtPath, fsConstants.X_OK);
const entrypointBytes = await readFile(thtPath, "utf8");
const lines = entrypointBytes.replaceAll("\r\n", "\n").split("\n");
const shebang = lines.shift() ?? "";
const pythonLexical = shebang.startsWith("#!") ? shebang.slice(2) : "";
const expectedBody = [
"import sys", "from tht.cli import app", "if __name__ == '__main__':",
" if sys.argv[0].endswith('.exe'):", " sys.argv[0] = sys.argv[0][:-4]",
" sys.exit(app())", "",
].join("\n");
const venvBin = join(repo, "harness", ".venv", "bin");
if (dirname(pythonLexical) !== venvBin || !/^python3(?:\.\d+)?$/.test(basename(pythonLexical))
|| realpathSync(pythonLexical) !== realpathSync(join(venvBin, "python"))
|| lines.join("\n") !== expectedBody) throw new Error("trusted THT generated entrypoint is invalid");
const sourceRoot = join(repo, "harness", "tht");
const pyproject = await readFile(join(repo, "harness", "pyproject.toml"), "utf8");
if (!/^tht\s*=\s*["']tht\.cli:app["']$/m.test(pyproject)) throw new Error("trusted THT console-script declaration is invalid");
const status = await runCommand({
executable: gitPath,
argv: ["-C", repo, "status", "--porcelain=v1", "--untracked-files=all", "--", "harness/tht", "harness/pyproject.toml"],
env: {
PATH: dirname(gitPath), HOME: "/nonexistent", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null",
GIT_CONFIG_COUNT: "1", GIT_CONFIG_KEY_0: "core.fsmonitor", GIT_CONFIG_VALUE_0: "false",
},
});
if (status.stdout !== "") throw new Error("trusted THT source is not tracked and clean");
const pythonVersion = basename(pythonLexical);
const sitePackages = join(repo, "harness", ".venv", "lib", pythonVersion, "site-packages");
const siteEntries = await readdir(sitePackages);
const allPthFiles = siteEntries.filter((name) => name.endsWith(".pth"));
const pthFiles = allPthFiles.filter((name) => /^__editable__\.tht-.*\.pth$/.test(name));
const finderFiles = siteEntries.filter((name) => /^__editable___tht_.*_finder\.py$/.test(name));
if (pthFiles.length !== 1 || allPthFiles.length !== 1 || finderFiles.length !== 1
|| siteEntries.some((name) => /^(?:sitecustomize|usercustomize|tht)\.py$/.test(name) || name === "tht")) {
throw new Error("trusted THT editable binding is ambiguous");
}
const pth = await readFile(join(sitePackages, pthFiles[0]), "utf8");
const finder = await readFile(join(sitePackages, finderFiles[0]), "utf8");
const finderModule = finderFiles[0].slice(0, -3);
if (pth.trim() !== `import ${finderModule}; ${finderModule}.install()`
|| !finder.includes(`MAPPING: dict[str, str] = {'tht': '${sourceRoot}'}`)
|| /\b(?:subprocess|socket|requests|httpx|urllib|multiprocessing)\b|\bos\.system\b|\bPopen\b/.test(finder)) {
throw new Error("trusted THT editable binding is invalid");
}
return {
gitPath, pythonPath, thtPath,
thtIdentity: {
entrypoint: "generated-console-script", entrypointSha256: sha256(entrypointBytes),
pythonPath: pythonLexical, pythonCanonicalPath: realpathSync(pythonLexical),
sourceRoot, sourceStatus: "tracked-clean", editableFinderSha256: sha256(finder),
},
};
}
const GIT_VERBS = new Set([
"--version", "add", "cat-file", "checkout", "clean", "clone", "commit", "config", "fetch", "for-each-ref",
"init", "ls-tree", "merge", "merge-base", "push", "remote", "reset", "rev-list", "rev-parse", "show",
"show-ref", "status", "symbolic-ref", "write-tree",
const FIXTURE_GIT_CONFIG = new Map([
["user.name", new Set(["P1 Fixture Curator", "P1 Context Curator"])],
["user.email", new Set(["p1-curator@example.invalid", "p1-context@example.invalid"])],
]);
function gitVerb(argv) {
if (argv[0] === "--version") return "--version";
let index = 0;
while (index < argv.length) {
if (["-C", "--git-dir", "--work-tree", "-c"].includes(argv[index])) { index += 2; continue; }
if (argv[index].startsWith("--git-dir=") || argv[index].startsWith("--work-tree=")) { index += 1; continue; }
return argv[index];
}
return undefined;
function ownedGitPath(value, runRoot) {
if (typeof value !== "string" || !isAbsolute(value) || value.includes("\0")) return false;
if (!runRoot) return true;
const lexical = resolve(value); const rel = relative(runRoot, lexical);
if (rel.startsWith("..") || isAbsolute(rel)) return false;
try { validateNoSymlinkAncestors(runRoot, lexical); } catch { return false; }
return true;
}
function validateGitInvocation(argv) {
const verb = gitVerb(argv);
if (!verb || !GIT_VERBS.has(verb)) throw new Error("Git command is prohibited");
if (argv.some((value) => /^[a-z][a-z0-9+.-]*:\/\//i.test(value) || /^[^/\s]+@[^:\s]+:/.test(value))) {
throw new Error("Git network URL is prohibited");
function ownedEmptyHooksPath(value, runRoot) {
if (!ownedGitPath(value, runRoot) || !/(?:^|\/)registry\/locks\/empty-hooks$/.test(value)) return false;
try {
const entry = lstatSync(value);
return entry.isDirectory() && !entry.isSymbolicLink() && realpathSync(value) === value;
} catch { return false; }
}
function safeGitOperand(value) { return typeof value === "string" && value.length > 0 && !value.startsWith("-") && !/[\0\n\r]/.test(value); }
function exactArray(value, expected) { return value.length === expected.length && value.every((item, index) => item === expected[index]); }
export function validateGitInvocation(argv, { runRoot } = {}) {
if (!Array.isArray(argv) || argv.some((value) => typeof value !== "string")) throw new Error("Git command is prohibited");
if (exactArray(argv, ["--version"])) return "--version";
let index = 0; let prefix;
if (["-C", "--git-dir"].includes(argv[0])) {
if (!ownedGitPath(argv[1], runRoot)) throw new Error("Git command is prohibited");
prefix = argv[0]; index = 2;
} else if (argv[0] === "-c") {
if (typeof argv[1] !== "string" || !argv[1].startsWith("core.hooksPath=")) throw new Error("Git command is prohibited");
const hooksPath = argv[1].slice("core.hooksPath=".length);
if (!ownedEmptyHooksPath(hooksPath, runRoot)) throw new Error("Git command is prohibited");
prefix = "hooks"; index = 2;
} else if (argv[0]?.startsWith("-")) throw new Error("Git command is prohibited");
const verb = argv[index]; const args = argv.slice(index + 1);
const branch = (value) => /^(?:main|invalid-context)$/.test(value ?? "");
const object = (value) => safeGitOperand(value) && !/^[a-z][a-z0-9+.-]*:\/\//i.test(value) && !/^[^/\s]+@[^:\s]+:/.test(value);
const ownedPair = (values) => values.length === 2 && values.every((value) => ownedGitPath(value, runRoot));
let valid = false;
switch (verb) {
case "init": valid = !prefix && args.length === 3 && args[0] === "--bare" && args[1] === "--initial-branch=main" && ownedGitPath(args[2], runRoot); break;
case "clone": valid = (!prefix && ownedPair(args))
|| (!prefix && args[0] === "--bare" && ownedPair(args.slice(1)))
|| (prefix === "hooks" && args.length === 6 && args[0] === "--branch" && branch(args[1]) && args[2] === "--single-branch" && args[3] === "--" && ownedPair(args.slice(4))); break;
case "config": valid = !prefix && args.length === 2 && FIXTURE_GIT_CONFIG.get(args[0])?.has(args[1]) === true; break;
case "add": valid = (!prefix || prefix === "hooks") && ((args.length === 1 && /^(?:workspace-content|workspace-content\/p1-filesystem\/evidence\/guide\.md)$/.test(args[0]))
|| (args.length === 2 && args[0] === "-A" && args[1] === "workspace-content/p1-filesystem/evidence")
|| (args[0] === "--" && args.length >= 2 && args.slice(1).every((value) => /^(?:workspaces|workspace-docs)\/[A-Za-z0-9./-]+$/.test(value)))); break;
case "commit": valid = (!prefix || prefix === "hooks") && args.length === 2 && args[0] === "-m" && /^(?:Bootstrap curated P1 content|Update curated Evidence only|Invalid contextual Evidence state|Publish workspace p1-(?:filesystem|http|s3))$/.test(args[1]); break;
case "push": valid = (!prefix || prefix === "hooks") && (exactArray(args, ["origin", "main"]) || exactArray(args, ["origin", "invalid-context"])
|| exactArray(args, ["-u", "origin", "invalid-context"]) || exactArray(args, ["origin", "HEAD:main"])); break;
case "checkout": valid = !prefix && exactArray(args, ["-b", "invalid-context"]); break;
case "fetch": valid = (!prefix || prefix === "hooks") && (exactArray(args, ["origin", "main"]) || (args.length === 3 && args[0] === "--no-tags" && args[1] === "origin" && branch(args[2]))); break;
case "remote": valid = prefix === "hooks" && args.length === 4 && exactArray(args.slice(0, 3), ["set-url", "origin", "--"]) && ownedGitPath(args[3], runRoot); break;
case "merge": valid = prefix === "hooks" && exactArray(args, ["--ff-only", "FETCH_HEAD"]); break;
case "merge-base": valid = prefix === "hooks" && exactArray(args, ["HEAD", "FETCH_HEAD"]); break;
case "reset": valid = (!prefix || prefix === "hooks") && args.length === 2 && args[0] === "--hard" && /^(?:origin\/main|refs\/remotes\/origin\/(?:main|invalid-context))$/.test(args[1]); break;
case "clean": valid = prefix === "hooks" && exactArray(args, ["-fd", "--", "workspaces", "workspace-docs"]); break;
case "status": valid = (!prefix && (exactArray(args, ["--porcelain=v1"]) || exactArray(args, ["--porcelain"])))
|| (prefix === "hooks" && exactArray(args, ["--porcelain"]))
|| (prefix === "-C" && exactArray(args, ["--porcelain=v1", "--untracked-files=all", "--", "harness/tht", "harness/pyproject.toml"])); break;
case "write-tree": valid = !prefix && args.length === 0; break;
case "show-ref": valid = (!prefix || prefix === "--git-dir" || prefix === "hooks") && args.length === 0; break;
case "symbolic-ref": valid = (!prefix || prefix === "hooks") && exactArray(args, ["--short", "HEAD"]); break;
case "rev-list": valid = ((prefix === "--git-dir" || prefix === "-C") && exactArray(args, ["--objects", "--all"]))
|| (prefix === "hooks" && exactArray(args, ["--left-right", "--count", "HEAD...@{upstream}"])); break;
case "ls-tree": valid = prefix === "hooks" && exactArray(args, ["-r", "--name-only", "HEAD", "--", "workspaces"]); break;
case "cat-file": valid = (!prefix || prefix === "--git-dir" || prefix === "-C" || prefix === "hooks") && args.length === 2
&& ((args[0] === "-e" || args[0] === "-t" || args[0] === "blob") && object(args[1])); break;
case "show": valid = prefix === "hooks" && args.length === 1 && object(args[0]); break;
case "rev-parse": valid = (!prefix || prefix === "--git-dir" || prefix === "hooks") && args.length === 1 && object(args[0]); break;
default: valid = false;
}
if (!valid) throw new Error("Git command is prohibited");
return verb;
}
function boundedChildEnvironment(value, expected) {
@@ -278,19 +380,50 @@ function boundedChildEnvironment(value, expected) {
for (const [key, value] of Object.entries(expected)) if (environment[key] !== value) throw new Error("child environment changed acceptance bounds");
return environment;
}
function sanitizedArgvLabels(argv = []) {
return Array.isArray(argv) ? argv.filter((value) => typeof value === "string").map((value) =>
isAbsolute(value) || value.includes(sep) ? "[path]" : /^[a-z]+:\/\//i.test(value) ? "[url]" : value.length > 80 ? "[value]" : value) : [];
}
function safeChildEvent(events, { surface = "child_process", api, executable, argv = [], outcome, detail, bounds }) {
events.push({
surface, api, executable: executable ? basename(executable) : undefined,
argvLabels: argv.map((value) => isAbsolute(value) || value.includes(sep) ? "[path]" : /^[a-z]+:\/\//i.test(value) ? "[url]" : value.length > 80 ? "[value]" : value),
argvLabels: sanitizedArgvLabels(argv),
outcome, ...(activeCommandCheckId ? { checkId: activeCommandCheckId } : {}), ...(detail ? { detail } : {}), ...(bounds ? { bounds } : {}),
});
}
function recordPolicyRejection({ executable, argv, api = "validation", detail = "policy" } = {}) {
const event = {
surface: "child_process", api, executable: typeof executable === "string" ? basename(executable) : undefined,
argvLabels: sanitizedArgvLabels(argv), outcome: "REJECTED", detail,
...(activeCommandCheckId ? { checkId: activeCommandCheckId } : {}),
};
if (activePolicyRejectionSink) activePolicyRejectionSink.push(event);
else if (commandEventSink) commandEventSink.push(event);
return event;
}
function policyError(message, details) { recordPolicyRejection(details); throw new Error(message); }
function validateThtInvocation(argv, { thtPath, runRoot, cwd } = {}) {
const configPath = Array.isArray(argv) ? argv[3] : undefined;
let configEntry;
try { configEntry = typeof configPath === "string" ? lstatSync(configPath) : undefined; } catch { configEntry = undefined; }
if (!configEntry || !exactArray(argv, ["config", "check", "-c", configPath]) || !ownedGitPath(configPath, runRoot)
|| !/\.ya?ml$/.test(configPath) || !configEntry.isFile() || configEntry.isSymbolicLink()
|| realpathSync(configPath) !== configPath || cwd !== dirname(dirname(dirname(thtPath)))) {
throw new Error("THT command is prohibited");
}
return "config-check";
}
export function installProductionSurfaceGuard({ gitPath, pythonPath, thtPath, runRoot, environment, originalFetch = globalThis.fetch }) {
export function installProductionSurfaceGuard({
gitPath, pythonPath, thtPath, thtIdentity, runRoot, environment,
originalFetch = globalThis.fetch, failPatchAt,
}) {
if (productionSurfaceOwner) throw new Error("production surface guard is already active");
for (const value of [gitPath, pythonPath, thtPath, runRoot]) if (!isAbsolute(value)) throw new Error("production guard paths must be absolute");
if (typeof originalFetch !== "function") throw new Error("global fetch is unavailable");
if (!thtIdentity || thtIdentity.sourceStatus !== "tracked-clean") throw new Error("trusted THT identity is absent");
if (failPatchAt !== undefined && (!Number.isInteger(failPatchAt) || failPatchAt < 1 || failPatchAt > 12)) throw new Error("invalid production patch failure probe");
const token = Symbol("p1-production-surface");
productionSurfaceOwner = token;
const events = [];
const originals = {
execFile: mutableChildProcess.execFile, spawn: mutableChildProcess.spawn,
@@ -299,12 +432,29 @@ export function installProductionSurfaceGuard({ gitPath, pythonPath, thtPath, ru
createSocket: mutableDgram.createSocket, Worker: mutableWorkerThreads.Worker,
dns: new Map(), dnsPromises: new Map(), dlopen: process.dlopen,
};
const resolveChild = (executable, argv) => {
for (const [name, value] of Object.entries(originals)) {
if (!["dns", "dnsPromises"].includes(name) && typeof value !== "function") throw new Error("production patch prerequisite is unavailable");
}
const validateOptions = (options, allowed, api) => {
if (!options || typeof options !== "object" || Array.isArray(options)) throw new Error(`${api} options are invalid`);
for (const key of Object.keys(options)) if (!allowed.has(key)) throw new Error(`${api} option is prohibited`);
if ("timeout" in options && (!Number.isSafeInteger(options.timeout) || options.timeout < 1 || options.timeout > 300_000)) throw new Error("command bounds are invalid");
if ("maxBuffer" in options && (!Number.isSafeInteger(options.maxBuffer) || options.maxBuffer < 1 || options.maxBuffer > MAX_OUTPUT)) throw new Error("command bounds are invalid");
if ("shell" in options && options.shell !== false) throw new Error(`${api} shell is prohibited`);
};
const resolveChild = (executable, argv, options, api) => {
const canonical = executable === "git" ? gitPath : executable === "python3" ? pythonPath : executable;
if (canonical === gitPath) return { executable: gitPath, kind: "git", verb: validateGitInvocation(argv) };
if (canonical === thtPath) return { executable: thtPath, kind: "tht" };
if (canonical === gitPath) {
validateGitInvocation(argv, { runRoot });
if (options.cwd !== undefined && !ownedGitPath(options.cwd, runRoot)) throw new Error("Git working directory is prohibited");
return { executable: gitPath, kind: "git" };
}
if (canonical === thtPath) {
validateThtInvocation(argv, { thtPath, runRoot, cwd: options.cwd });
return { executable: thtPath, kind: "tht" };
}
if (canonical === pythonPath) {
if (argv.length !== 3 || argv[0] !== "-c" || argv[1] !== PYTHON_LOCK_HOLDER_PROGRAM
if (api !== "spawn" || argv.length !== 3 || argv[0] !== "-c" || argv[1] !== PYTHON_LOCK_HOLDER_PROGRAM
|| !isAbsolute(argv[2]) || relative(runRoot, argv[2]).startsWith("..") || basename(argv[2]) !== "repository.lock") {
throw new Error("child command is prohibited");
}
@@ -312,18 +462,21 @@ export function installProductionSurfaceGuard({ gitPath, pythonPath, thtPath, ru
}
throw new Error("child command is prohibited");
};
const rejectChild = (api, args) => {
safeChildEvent(events, { api, executable: typeof args[0] === "string" ? args[0] : undefined, outcome: "REJECTED" });
throw new Error("child command is prohibited");
const rejectChild = (api, args, message = "child command is prohibited") => {
safeChildEvent(events, { api, executable: typeof args[0] === "string" ? args[0] : undefined, argv: Array.isArray(args[1]) ? args[1] : [], outcome: "REJECTED" });
throw new Error(message);
};
const guardedExecFile = function guardedExecFile(executable, argv, options, callback) {
if (!Array.isArray(argv)) return rejectChild("execFile", [executable]);
if (typeof options === "function") { callback = options; options = {}; }
options ??= {};
let resolved;
try { resolved = resolveChild(executable, argv); boundedChildEnvironment(options.env, environment); }
catch (error) { safeChildEvent(events, { api: "execFile", executable, argv, outcome: "REJECTED" }); throw error; }
const bounded = { ...options, env: options.env ?? environment, timeout: Math.min(options.timeout ?? 30_000, 300_000), maxBuffer: Math.min(options.maxBuffer ?? MAX_OUTPUT, MAX_OUTPUT), shell: false };
try {
validateOptions(options, new Set(["cwd", "env", "timeout", "maxBuffer", "encoding", "shell"]), "execFile");
resolved = resolveChild(executable, argv, options, "execFile");
boundedChildEnvironment(options.env, environment);
} catch (error) { safeChildEvent(events, { api: "execFile", executable, argv, outcome: "REJECTED" }); throw error; }
const bounded = { ...options, env: options.env ?? environment, timeout: options.timeout ?? 30_000, maxBuffer: options.maxBuffer ?? MAX_OUTPUT, shell: false };
safeChildEvent(events, { api: "execFile", executable: resolved.executable, argv, outcome: "STARTED", detail: resolved.kind,
bounds: { timeoutMs: bounded.timeout, maxOutputBytes: bounded.maxBuffer, environment: "owned" } });
return originals.execFile(resolved.executable, argv, bounded, (error, stdout, stderr) => {
@@ -337,8 +490,12 @@ export function installProductionSurfaceGuard({ gitPath, pythonPath, thtPath, ru
const guardedSpawn = function guardedSpawn(executable, argv, options = {}) {
if (!Array.isArray(argv)) return rejectChild("spawn", [executable]);
let resolved;
try { resolved = resolveChild(executable, argv); boundedChildEnvironment(options.env, environment); }
catch (error) { safeChildEvent(events, { api: "spawn", executable, argv, outcome: "REJECTED" }); throw error; }
try {
validateOptions(options, new Set(["cwd", "env", "stdio", "shell"]), "spawn");
if ("stdio" in options && JSON.stringify(options.stdio) !== JSON.stringify(["pipe", "pipe", "pipe"])) throw new Error("spawn stdio is prohibited");
resolved = resolveChild(executable, argv, options, "spawn");
boundedChildEnvironment(options.env, environment);
} catch (error) { safeChildEvent(events, { api: "spawn", executable, argv, outcome: "REJECTED" }); throw error; }
const bounded = { ...options, env: options.env ?? environment, shell: false };
safeChildEvent(events, { api: "spawn", executable: resolved.executable, argv, outcome: "STARTED", detail: resolved.kind,
bounds: { timeoutMs: 300_000, maxOutputBytes: MAX_OUTPUT, environment: "owned" } });
@@ -351,100 +508,104 @@ export function installProductionSurfaceGuard({ gitPath, pythonPath, thtPath, ru
child.once("error", () => { clearTimeout(timer); });
return child;
};
const owned = { execFile: guardedExecFile, spawn: guardedSpawn, child: new Map(), dns: new Map(), dnsPromises: new Map() };
mutableChildProcess.execFile = guardedExecFile;
mutableChildProcess.spawn = guardedSpawn;
for (const api of ["exec", "execSync", "execFileSync", "spawnSync", "fork"]) {
const wrapper = (...args) => rejectChild(api, args); owned.child.set(api, wrapper); mutableChildProcess[api] = wrapper;
}
const childWrappers = new Map();
for (const api of ["exec", "execSync", "execFileSync", "spawnSync", "fork"]) childWrappers.set(api, (...args) => rejectChild(api, args));
const guardedCreateSocket = (..._args) => { safeChildEvent(events, { surface: "dgram", api: "createSocket", outcome: "REJECTED" }); throw new Error("prohibited production surface: dgram"); };
class ProhibitedWorker { constructor() { safeChildEvent(events, { surface: "worker_threads", api: "Worker", outcome: "REJECTED" }); throw new Error("prohibited production surface: worker_threads"); } }
mutableDgram.createSocket = guardedCreateSocket;
mutableWorkerThreads.Worker = ProhibitedWorker;
const dnsWrappers = new Map(); const dnsPromiseWrappers = new Map();
for (const name of ["lookup", "resolve", "resolve4", "resolve6", "resolveAny", "resolveCaa", "resolveCname", "resolveMx", "resolveNaptr", "resolveNs", "resolvePtr", "resolveSoa", "resolveSrv", "resolveTxt", "reverse", "Resolver"]) {
if (typeof mutableDns[name] !== "function") continue;
originals.dns.set(name, mutableDns[name]);
const original = originals.dns.get(name);
const wrapper = (...args) => {
const original = mutableDns[name]; originals.dns.set(name, original);
dnsWrappers.set(name, (...args) => {
if (name === "lookup" && ["127.0.0.1", "::1"].includes(args[0])) {
safeChildEvent(events, { surface: "dns", api: name, outcome: "PASS", detail: "owned-loopback-literal" });
return original(...args);
safeChildEvent(events, { surface: "dns", api: name, outcome: "PASS", detail: "owned-loopback-literal" }); return original(...args);
}
safeChildEvent(events, { surface: "dns", api: name, outcome: "REJECTED" });
throw new Error("prohibited production surface: dns");
};
owned.dns.set(name, wrapper); mutableDns[name] = wrapper;
safeChildEvent(events, { surface: "dns", api: name, outcome: "REJECTED" }); throw new Error("prohibited production surface: dns");
});
}
for (const [name, value] of Object.entries(mutableDns.promises ?? {})) if (typeof value === "function") {
originals.dnsPromises.set(name, value);
const original = originals.dnsPromises.get(name);
const wrapper = async (...args) => {
dnsPromiseWrappers.set(name, async (...args) => {
if (name === "lookup" && ["127.0.0.1", "::1"].includes(args[0])) {
safeChildEvent(events, { surface: "dns", api: `promises.${name}`, outcome: "PASS", detail: "owned-loopback-literal" });
return await original(...args);
safeChildEvent(events, { surface: "dns", api: `promises.${name}`, outcome: "PASS", detail: "owned-loopback-literal" }); return await value(...args);
}
safeChildEvent(events, { surface: "dns", api: `promises.${name}`, outcome: "REJECTED" });
throw new Error("prohibited production surface: dns");
};
owned.dnsPromises.set(name, wrapper); mutableDns.promises[name] = wrapper;
safeChildEvent(events, { surface: "dns", api: `promises.${name}`, outcome: "REJECTED" }); throw new Error("prohibited production surface: dns");
});
}
const guardedDlopen = (..._args) => { safeChildEvent(events, { surface: "native_addon", api: "dlopen", outcome: "REJECTED" }); throw new Error("prohibited production surface: native addon"); };
process.dlopen = guardedDlopen;
syncBuiltinESMExports();
const network = installNetworkGuard(originalFetch);
activeExecutablePolicy = { gitPath, pythonPath, thtPath };
let restored = false;
const changes = []; let network; let restored = false; let patchStep = 0;
const assign = (target, key, value) => { const original = target[key]; target[key] = value; changes.push({ target, key, value, original }); };
const checkpoint = () => { patchStep += 1; if (failPatchAt === patchStep) throw new Error("injected production patch failure"); };
const rollback = () => {
const errors = [];
if (network) { try { network.restore(); } catch (error) { errors.push(error); } network = undefined; }
for (const { target, key, original } of [...changes].reverse()) { try { target[key] = original; } catch (error) { errors.push(error); } }
try { syncBuiltinESMExports(); } catch (error) { errors.push(error); }
if (productionSurfaceOwner === token) productionSurfaceOwner = undefined;
if (activePolicyRejectionSink === events) activePolicyRejectionSink = undefined;
if (activeExecutablePolicy?.owner === token) activeExecutablePolicy = undefined;
return errors;
};
try {
productionSurfaceOwner = token; checkpoint();
assign(mutableChildProcess, "execFile", guardedExecFile); checkpoint();
assign(mutableChildProcess, "spawn", guardedSpawn); checkpoint();
for (const [api, wrapper] of childWrappers) assign(mutableChildProcess, api, wrapper); checkpoint();
assign(mutableDgram, "createSocket", guardedCreateSocket); checkpoint();
assign(mutableWorkerThreads, "Worker", ProhibitedWorker); checkpoint();
for (const [name, wrapper] of dnsWrappers) assign(mutableDns, name, wrapper); checkpoint();
for (const [name, wrapper] of dnsPromiseWrappers) assign(mutableDns.promises, name, wrapper); checkpoint();
assign(process, "dlopen", guardedDlopen); checkpoint();
syncBuiltinESMExports(); checkpoint();
network = installNetworkGuard(originalFetch); checkpoint();
activePolicyRejectionSink = events;
activeExecutablePolicy = { gitPath, pythonPath, thtPath, thtIdentity, runRoot, owner: token }; checkpoint();
} catch (error) {
const rollbackErrors = rollback();
if (rollbackErrors.length) throw new Error("production surface guard installation rollback failed", { cause: error });
throw error;
}
return {
events, externalAttempts: network.externalAttempts,
addOwnedOrigin: network.addOwnedOrigin, hasOwnedOrigin: network.hasOwnedOrigin,
restore() {
if (restored) throw new Error("production surface guard restored twice");
restored = true;
let tampered = productionSurfaceOwner !== token;
const ownsToken = productionSurfaceOwner === token;
const restoreOwned = (target, key, wrapper, original) => {
if (target[key] !== wrapper) tampered = true;
if (ownsToken) target[key] = original;
};
const errors = [];
try { network.restore(); } catch (error) { errors.push(error); }
restoreOwned(mutableChildProcess, "execFile", guardedExecFile, originals.execFile);
restoreOwned(mutableChildProcess, "spawn", guardedSpawn, originals.spawn);
for (const [api, wrapper] of owned.child) restoreOwned(mutableChildProcess, api, wrapper, originals[api]);
restoreOwned(mutableDgram, "createSocket", guardedCreateSocket, originals.createSocket);
restoreOwned(mutableWorkerThreads, "Worker", ProhibitedWorker, originals.Worker);
for (const [name, wrapper] of owned.dns) restoreOwned(mutableDns, name, wrapper, originals.dns.get(name));
for (const [name, wrapper] of owned.dnsPromises) restoreOwned(mutableDns.promises, name, wrapper, originals.dnsPromises.get(name));
restoreOwned(process, "dlopen", guardedDlopen, originals.dlopen); syncBuiltinESMExports();
if (productionSurfaceOwner === token) productionSurfaceOwner = undefined;
if (activeExecutablePolicy?.gitPath === gitPath) activeExecutablePolicy = undefined;
let tampered = productionSurfaceOwner !== token || activePolicyRejectionSink !== events || activeExecutablePolicy?.owner !== token;
for (const { target, key, value } of changes) if (target[key] !== value) tampered = true;
const errors = rollback();
if (tampered || errors.length) throw new Error("production surface guard ownership restoration failed");
},
};
}
export async function runCommand(options) {
if (!options || typeof options !== "object" || Array.isArray(options)) throw new Error("command requires an options object");
const details = () => ({ executable: options && typeof options === "object" ? options.executable : undefined,
argv: options && typeof options === "object" ? options.argv : undefined, api: "runCommand", detail: "policy" });
if (!options || typeof options !== "object" || Array.isArray(options)) policyError("command requires an options object", details());
const allowed = new Set(["executable", "argv", "cwd", "env", "timeoutMs", "stdin", "maxOutputBytes"]);
for (const key of Object.keys(options)) if (!allowed.has(key)) throw new Error(`unsupported command option ${key}`);
for (const key of Object.keys(options)) if (!allowed.has(key)) policyError(`unsupported command option ${key}`, details());
const { executable, argv, cwd, env, timeoutMs = 30_000, stdin, maxOutputBytes = MAX_OUTPUT } = options;
if (typeof executable !== "string" || !isAbsolute(executable) || /[;&|`$><\n\r]/.test(executable)) throw new Error("command executable is invalid");
if (typeof executable !== "string" || !isAbsolute(executable) || /[;&|`$><\n\r]/.test(executable)) policyError("command executable is invalid", details());
let canonical;
try { canonical = realpathSync(executable); } catch { throw new Error("command executable is not allowlisted"); }
try { canonical = realpathSync(executable); } catch { policyError("command executable is not allowlisted", details()); }
const allowedGit = activeExecutablePolicy?.gitPath ?? resolveTrustedSystemExecutableSync("git");
const allowedTht = activeExecutablePolicy?.thtPath;
if (canonical !== allowedGit && canonical !== allowedTht) throw new Error("command executable is not allowlisted");
if (!Array.isArray(argv) || argv.some((value) => typeof value !== "string")) throw new Error("command argv must be a string array");
if (canonical === allowedGit) validateGitInvocation(argv);
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 300_000 || !Number.isSafeInteger(maxOutputBytes) || maxOutputBytes < 1 || maxOutputBytes > MAX_OUTPUT) throw new Error("command bounds are invalid");
if (canonical !== allowedGit && canonical !== allowedTht) policyError("command executable is not allowlisted", details());
if (!Array.isArray(argv) || argv.some((value) => typeof value !== "string")) policyError("command argv must be a string array", details());
try {
if (canonical === allowedGit) validateGitInvocation(argv, { runRoot: activeExecutablePolicy?.runRoot });
if (canonical === allowedTht) validateThtInvocation(argv, { thtPath: allowedTht, runRoot: activeExecutablePolicy.runRoot, cwd });
} catch (error) { policyError(error.message, details()); }
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 300_000 || !Number.isSafeInteger(maxOutputBytes) || maxOutputBytes < 1 || maxOutputBytes > MAX_OUTPUT) {
policyError("command bounds are invalid", details());
}
return await new Promise((resolvePromise, reject) => {
const child = mutableChildProcess.execFile(canonical, argv, { cwd, env, timeout: timeoutMs, maxBuffer: maxOutputBytes, encoding: "utf8", shell: false }, (error, stdout, stderr) => {
const code = error && typeof error.code === "number" ? error.code : error ? 1 : 0;
const result = { code, stdout: stdout ?? "", stderr: stderr ?? "" };
if (commandEventSink) commandEventSink.push({
executable: basename(canonical),
argvLabels: argv.map((value) => isAbsolute(value) || value.includes(sep) ? "[path]" : /^[a-z]+:\/\//i.test(value) ? "[url]" : value.length > 80 ? "[value]" : value),
outcome: error ? "FAIL" : "PASS",
executable: basename(canonical), argvLabels: sanitizedArgvLabels(argv), outcome: error ? "FAIL" : "PASS",
...(activeCommandCheckId ? { checkId: activeCommandCheckId } : {}),
});
if (error) Object.assign(error, { result });
@@ -618,8 +779,15 @@ export function installNetworkGuard(fetchImplementation = globalThis.fetch) {
}
return originalConnect.apply(this, args);
};
globalThis.fetch = guardedFetch;
Socket.prototype.connect = guardedSocketConnect;
let fetchChanged = false; let socketChanged = false;
try {
globalThis.fetch = guardedFetch; fetchChanged = true;
Socket.prototype.connect = guardedSocketConnect; socketChanged = true;
} catch (error) {
if (socketChanged) Socket.prototype.connect = originalConnect;
if (fetchChanged) globalThis.fetch = originalFetch;
throw error;
}
let restored = false;
return {
externalAttempts,
@@ -893,9 +1061,7 @@ export function buildSafeEnvironment({ ambient = process.env, fixture = {} } = {
}
async function setupContext(run, repositoryRoot, env, ctx = {}) {
const executables = await resolveProductionExecutables({
repositoryRoot, thtBin: env.THT_BIN ?? join(repositoryRoot, "harness", ".venv", "bin", "tht"),
});
const executables = await resolveProductionExecutables({ repositoryRoot });
const harnessDir = realpathSync(join(repositoryRoot, "harness"));
const gitTracePath = join(run.root, "logs", "production-git-trace.jsonl");
const ownedHome = join(run.root, "installation", "runtime", "acceptance-home");
@@ -907,9 +1073,10 @@ async function setupContext(run, repositoryRoot, env, ctx = {}) {
PATH: executablePath, HOME: ownedHome, TMPDIR: ownedTmp,
GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", GIT_TERMINAL_PROMPT: "0",
GIT_ASKPASS: "/bin/false", SSH_ASKPASS: "/bin/false", GIT_ALLOW_PROTOCOL: "file", GIT_PROTOCOL_FROM_USER: "0",
GIT_CONFIG_COUNT: "3", GIT_CONFIG_KEY_0: "commit.gpgSign", GIT_CONFIG_VALUE_0: "false",
GIT_CONFIG_COUNT: "4", GIT_CONFIG_KEY_0: "commit.gpgSign", GIT_CONFIG_VALUE_0: "false",
GIT_CONFIG_KEY_1: "tag.gpgSign", GIT_CONFIG_VALUE_1: "false",
GIT_CONFIG_KEY_2: "credential.helper", GIT_CONFIG_VALUE_2: "",
GIT_CONFIG_KEY_3: "core.fsmonitor", GIT_CONFIG_VALUE_3: "false", GIT_PAGER: "/bin/cat",
HOST: "127.0.0.1", PORT: "0", AUTH_MODE: "none", THT_BIN: executables.thtPath,
THT_HARNESS_DIR: harnessDir, THT_DATA_ROOT: join(run.root, "installation", "data"),
SETTINGS_FILE: join(run.root, "installation", "data", "settings.json"),
@@ -1397,7 +1564,10 @@ function productionChecks(ctx) {
const gitTraceProof = await assertProductionGitTrace(ctx);
assert(present.length === 0 && prohibitedRoutesCalled.length === 0 && prohibitedCommands.length === 0, "prohibited P1 scope operation observed");
assert(ctx.networkGuard.externalAttempts.length === 0, "external network connection attempted");
const rejectedSurfaces = ctx.networkGuard.events.filter(({ outcome }) => outcome === "REJECTED");
const rejectedSurfaces = [
...ctx.networkGuard.events,
...(commandEventSink ?? []).filter((event) => event.outcome === "REJECTED" && !ctx.networkGuard.events.includes(event)),
].filter(({ outcome }) => outcome === "REJECTED");
const rejectedChildren = rejectedSurfaces.filter(({ surface }) => surface === "child_process");
const childKinds = new Set(ctx.networkGuard.events.filter(({ surface }) => surface === "child_process").map(({ detail }) => detail).filter(Boolean));
assert(rejectedSurfaces.length === 0 && rejectedChildren.length === 0 && ["git", "python-lock-holder", "tht"].every((kind) => childKinds.has(kind)),
@@ -1589,9 +1759,11 @@ export async function runIntegration({
attachResultArtifact(results, "preflight", commandArtifact);
if (ctx.networkGuard) {
const executablePolicy = {};
for (const [name, executablePath] of Object.entries(ctx.executables)) {
for (const name of ["gitPath", "pythonPath", "thtPath"]) {
const executablePath = ctx.executables[name];
executablePolicy[name] = { path: executablePath, sha256: sha256(await readFile(executablePath)) };
}
executablePolicy.thtIdentity = ctx.executables.thtIdentity;
const childArtifact = await evidence(run, "logs/production-child-events.json", {
executablePolicy, environmentPolicy: {
PATH: ctx.env.PATH, HOME: ctx.env.HOME, TMPDIR: ctx.env.TMPDIR,
+139 -20
View File
@@ -280,15 +280,13 @@ test("command helper accepts only executable plus separate argv", async () => {
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 invalid/);
const repositoryRoot = await fakeRepository();
const executable = join(repositoryRoot, "executable with spaces");
const scratchRoot = await fakeRepository();
const executable = join(scratchRoot, "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 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 repositoryRoot = await realpath(join(dirname(fileURLToPath(import.meta.url)), "..", ".."));
const { gitPath } = await resolveProductionExecutables({ repositoryRoot });
const result = await runCommand({ executable: gitPath, argv: ["--version"] });
assert.match(result.stdout, /^git version /);
assert.equal(result.code, 0);
@@ -350,7 +348,7 @@ test("announce callback observes PASS and manual pending before non-keep cleanup
test("public wrapper replaces ambient environment before invoking the runner", async () => {
const wrapper = await readFile(join(dirname(fileURLToPath(import.meta.url)), "..", "..", "scripts", "p1-acceptance.sh"), "utf8");
assert.match(wrapper, /safe_env=\(env -i/);
assert.match(wrapper, /safe_env=\(\/usr\/bin\/env -i/);
assert.match(wrapper, /P1_ACCEPTANCE_FAIL_AT/);
assert.doesNotMatch(wrapper, /export THT_BIN/);
});
@@ -473,14 +471,11 @@ test("runIntegration fails closed when a later duplicate overwrites stale artifa
});
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 runRoot = await fakeRepository();
const repositoryRoot = await realpath(join(dirname(fileURLToPath(import.meta.url)), "..", ".."));
const executables = await resolveProductionExecutables({ repositoryRoot });
const guard = installProductionSurfaceGuard({
...executables, runRoot: repositoryRoot, environment: { ...process.env }, originalFetch: globalThis.fetch,
...executables, runRoot, environment: { ...process.env }, originalFetch: globalThis.fetch,
});
try {
assert.throws(() => dgram.createSocket("udp4"), /prohibited production surface/);
@@ -576,16 +571,140 @@ test("environment tampering fails the audit and restores the caller environment"
});
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 runRoot = await fakeRepository();
const repositoryRoot = await realpath(join(dirname(fileURLToPath(import.meta.url)), "..", ".."));
const executables = await resolveProductionExecutables({ repositoryRoot });
const originalFetch = globalThis.fetch;
const guard = installProductionSurfaceGuard({ ...executables, runRoot: repositoryRoot, environment: { ...process.env }, originalFetch });
const guard = installProductionSurfaceGuard({ ...executables, runRoot, 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);
});
test("Git grammar rejects helper, config, alias, and network-capable spellings with one event each", async () => {
const repositoryRoot = await realpath(join(dirname(fileURLToPath(import.meta.url)), "..", ".."));
const runRoot = await fakeRepository();
const executables = await resolveProductionExecutables({ repositoryRoot, thtBin: "/tmp/hostile-tht" });
const guard = installProductionSurfaceGuard({ ...executables, runRoot, environment: { ...process.env } });
const source = join(runRoot, "source.git");
const destination = join(runRoot, "destination");
const marker = join(runRoot, "helper-ran");
await execFileAsync(executables.gitPath, ["init", "--bare", source]);
const helper = join(runRoot, "upload-helper");
await writeFile(helper, `#!/bin/sh\nprintf ran > "${marker}"\nexit 99\n`, { mode: 0o700 });
const prohibited = [
["clone", `--upload-pack=${helper}`, source, destination],
["clone", "--receive-pack=/tmp/helper", source, destination],
["--exec-path=/tmp", "status"],
["-c", "alias.status=!touch /tmp/pwn", "status"],
["-c", "core.hooksPath=/tmp/hooks", "status"],
["-c", "diff.external=/tmp/helper", "status"],
["config", "filter.bad.clean", "/tmp/helper"],
["ls-remote", "https://example.com/repo.git"],
];
try {
for (const argv of prohibited) {
const before = guard.events.length;
await assert.rejects(runCommand({ executable: executables.gitPath, argv }), /Git command is prohibited/);
assert.equal(guard.events.length - before, 1);
assert.equal(guard.events.at(-1).outcome, "REJECTED");
}
await assert.rejects(lstat(marker));
} finally { guard.restore(); }
});
test("production executables ignore ambient THT and bind the generated tht entrypoint to reviewed source", async () => {
const repositoryRoot = await realpath(join(dirname(fileURLToPath(import.meta.url)), "..", ".."));
const hostile = join(await fakeRepository(), "tht");
await writeFile(hostile, "#!/bin/sh\nexit 0\n", { mode: 0o700 });
const executables = await resolveProductionExecutables({ repositoryRoot, thtBin: hostile });
assert.equal(executables.thtPath, join(repositoryRoot, "harness", ".venv", "bin", "tht"));
assert.equal(executables.thtIdentity.sourceRoot, join(repositoryRoot, "harness", "tht"));
assert.equal(executables.thtIdentity.sourceStatus, "tracked-clean");
assert.equal(executables.thtIdentity.entrypoint, "generated-console-script");
assert.match(executables.thtIdentity.pythonPath, /python3(?:\.\d+)?$/);
});
test("tht accepts only config check for one owned rendered yaml", async () => {
const repositoryRoot = await realpath(join(dirname(fileURLToPath(import.meta.url)), "..", ".."));
const runRoot = await fakeRepository();
const rendered = join(runRoot, "rendered", "workspace.yaml");
await mkdir(dirname(rendered), { recursive: true });
await writeFile(rendered, "profile: acceptance\n");
const executables = await resolveProductionExecutables({ repositoryRoot });
const guard = installProductionSurfaceGuard({ ...executables, runRoot, environment: { ...process.env } });
const childProcess = await import("node:child_process");
try {
for (const argv of [
["config", "check"], ["config", "check", "-c", "/tmp/unowned.yaml"],
["doctor"], ["config", "check", "-c", rendered, "--extra"],
]) {
const before = guard.events.length;
assert.throws(() => childProcess.execFile(executables.thtPath, argv), /THT command is prohibited/);
assert.equal(guard.events.length - before, 1);
}
} finally { guard.restore(); }
});
test("production guard installation rolls back every patch and owner on every injected patch failure", async () => {
const repositoryRoot = await realpath(join(dirname(fileURLToPath(import.meta.url)), "..", ".."));
const runRoot = await fakeRepository();
const executables = await resolveProductionExecutables({ repositoryRoot });
const childProcess = await import("node:child_process");
const originalSpawn = childProcess.spawn;
const originalDgram = dgram.createSocket;
const originalFetch = globalThis.fetch;
for (let failPatchAt = 1; failPatchAt <= 12; failPatchAt += 1) {
assert.throws(() => installProductionSurfaceGuard({
...executables, runRoot, environment: { ...process.env }, failPatchAt,
}), /injected production patch failure/);
assert.equal(childProcess.spawn, originalSpawn);
assert.equal(dgram.createSocket, originalDgram);
assert.equal(globalThis.fetch, originalFetch);
const reacquired = installProductionSurfaceGuard({ ...executables, runRoot, environment: { ...process.env } });
reacquired.restore();
}
});
test("command bounds reject zero, negative, fractional, and nonnumeric timeouts with one sanitized event", async () => {
const repositoryRoot = await realpath(join(dirname(fileURLToPath(import.meta.url)), "..", ".."));
const runRoot = await fakeRepository();
const executables = await resolveProductionExecutables({ repositoryRoot });
const guard = installProductionSurfaceGuard({ ...executables, runRoot, environment: { ...process.env } });
try {
for (const timeoutMs of [0, -1, 1.5, NaN]) {
const before = guard.events.length;
await assert.rejects(runCommand({ executable: executables.gitPath, argv: ["--version"], timeoutMs }), /command bounds are invalid/);
assert.equal(guard.events.length - before, 1);
}
} finally { guard.restore(); }
});
test("public wrapper has no ambient command resolution and isolates the build and runner", async () => {
const wrapper = await readFile(join(dirname(fileURLToPath(import.meta.url)), "..", "..", "scripts", "p1-acceptance.sh"), "utf8");
assert.doesNotMatch(wrapper, /command\s+-v/);
assert.doesNotMatch(wrapper, /\b(?:node|npm)\s+--prefix/);
assert.match(wrapper, /env -i/);
assert.match(wrapper, /npm-cli\.js/);
assert.match(wrapper, /"\$node_path" "\$npm_path"/);
});
test("hostile PATH Node npm and THT substitutes never execute before a real wrapper integration", async () => {
const hostileRoot = await fakeRepository();
const marker = join(hostileRoot, "ambient-tool-ran");
for (const name of ["node", "npm", "tht"]) {
const path = join(hostileRoot, name);
await writeFile(path, `#!/bin/sh\nprintf '%s' '${name}' >> '${marker}'\nexit 97\n`, { mode: 0o700 });
await chmod(path, 0o700);
}
const wrapper = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "scripts", "p1-acceptance.sh");
const { stdout } = await execFileAsync(wrapper, ["integration"], {
env: { ...process.env, PATH: hostileRoot, THT_BIN: join(hostileRoot, "tht") }, timeout: 120_000, maxBuffer: 4 * 1024 * 1024,
});
assert.match(stdout, /automated integration: PASS/);
await assert.rejects(lstat(marker));
});