#!/usr/bin/env node import { createHash, randomBytes } from "node:crypto"; import { accessSync, closeSync, constants as fsConstants, existsSync, fsyncSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, realpathSync, statSync, } from "node:fs"; import { access, chmod, lstat, mkdir, open, readFile, readdir, realpath, rename, rm, stat, symlink, writeFile, } from "node:fs/promises"; import { tmpdir } from "node:os"; import { createRequire, syncBuiltinESMExports } from "node:module"; import { Socket, isIP } from "node:net"; import { basename, dirname, isAbsolute, join, relative, resolve, sep, } from "node:path"; import { fileURLToPath } from "node:url"; import { promisify } from "node:util"; const require = createRequire(import.meta.url); const mutableChildProcess = require("node:child_process"); const mutableDgram = require("node:dgram"); const mutableDns = require("node:dns"); 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}$/; const HEX40 = /^[0-9a-f]{40}$/; const HEX64 = /^[0-9a-f]{64}$/; const ISO_UTC = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; const SAFE_RELATIVE = /^(?!\/)(?!.*(?:^|\/)\.\.(?:\/|$))(?!.*\\)[A-Za-z0-9._/-]+$/; const COMMAND = /^[A-Za-z0-9._+-]+$/; export const CHECK_IDS = Object.freeze([ "preflight", "clean_state", "ownership", "local_git_bootstrap", "http_validate_publish_pull_read_export", "same_revision_git_objects", "content_only_revision", "snapshot_and_docs", "runtime_render_determinism", "tht_config_check", "negative_schema_cases", "negative_context_case", "no_p1_scope_artifacts", "secret_scan", "cleanup_confinement", ]); const TOPOLOGY = [ "remote.git", "author", "installation/registry", "installation/data", "installation/runtime", "fixture-secrets", "fixtures/descriptors", "fixtures/requests", "requests", "responses", "exports/raw", "exports/extracted", "rendered", "logs", ]; const ZIP_FILES = ["manifest.json", "workspace.yaml", "contract.env.example", "README.md"]; const MAX_OUTPUT = 16 * 1024 * 1024; const PYTHON_LOCK_HOLDER_PROGRAM = [ "import fcntl, os, sys", "fd = os.open(sys.argv[1], os.O_RDWR | os.O_CREAT | getattr(os, 'O_NOFOLLOW', 0), 0o600)", "try:", " fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)", "except BlockingIOError:", " sys.exit(73)", "sys.stdout.write('locked\\n')", "sys.stdout.flush()", "sys.stdin.buffer.read()", ].join("\n"); const modulePath = fileURLToPath(import.meta.url); const defaultRepositoryRoot = realpathSync(resolve(dirname(modulePath), "../..")); function nowIso() { return new Date().toISOString(); } function sha256(value) { return createHash("sha256").update(value).digest("hex"); } export function scalarSecretBytes(value) { if (typeof value !== "string" || value.length === 0 || /\s|\0/.test(value)) throw new Error("scalar fixture secret is invalid"); return Buffer.from(value); } function canonicalRoot(repositoryRoot) { return realpathSync(repositoryRoot); } export function canonicalIntegrationBase(repositoryRoot) { return join(canonicalRoot(repositoryRoot), ".artifacts", "p1-integration"); } export function validateRunRoot(repositoryRoot, runRoot, runId) { if (!RUN_ID.test(runId)) throw new Error("invalid owned run id"); const base = canonicalIntegrationBase(repositoryRoot); const lexical = resolve(runRoot); if (dirname(lexical) !== base || basename(lexical) !== runId) throw new Error("run root is not a direct integration child"); return lexical; } function validateNoSymlinkAncestors(repositoryRoot, target) { const repo = canonicalRoot(repositoryRoot); const rel = relative(repo, target); if (rel.startsWith("..") || isAbsolute(rel)) throw new Error("path leaves repository"); let cursor = repo; for (const part of rel.split(sep).filter(Boolean)) { cursor = join(cursor, part); if (!existsSync(cursor)) break; const entry = lstatSync(cursor); if (entry.isSymbolicLink()) throw new Error("owned path ancestor is a symlink"); } } async function atomicWrite(path, bytes, mode = 0o600) { await mkdir(dirname(path), { recursive: true }); const staging = join(dirname(path), `.${basename(path)}.${randomBytes(16).toString("hex")}.tmp`); let handle; try { handle = await open(staging, "wx", mode); await handle.writeFile(bytes); await handle.sync(); await handle.close(); handle = undefined; await rename(staging, path); const directory = openSync(dirname(path), fsConstants.O_RDONLY); try { fsyncSync(directory); } finally { closeSync(directory); } } catch (error) { let failure = error; if (handle) { try { await handle.close(); } catch (closeError) { failure = closeError; } } try { await rm(staging, { force: true }); } catch (cleanupError) { failure = cleanupError; } throw failure; } } function exactOwnedResources(run) { const contextual = join(run.root, "installation", "runtime", "contextual"); return [ run.root, join(run.root, "remote.git"), join(run.root, "author"), join(run.root, "installation", "registry"), join(run.root, "installation", "data"), join(run.root, "installation", "runtime"), contextual, join(contextual, "remote.git"), join(contextual, "author"), join(contextual, "registry"), join(contextual, "data"), join(contextual, "runtime"), ]; } function initialListeners(pid) { return ["primary", "contextual"].map((name) => ({ name, kind: "fastify", host: "127.0.0.1", requestedPort: 0, pid, state: "not_started", })); } function ownership(run, listeners = run.listeners) { return { schemaVersion: 1, runId: run.runId, runNonce: run.nonce, root: run.root, repositoryRoot: run.repositoryRoot, startedAt: run.startedAt, pid: run.pid, listeners, resources: exactOwnedResources(run), }; } async function writeOwnership(run, listenerUpdate) { const listeners = listenerUpdate ? run.listeners.map((listener) => listener.name === listenerUpdate.name ? listenerUpdate : listener) : run.listeners; await atomicWrite(join(run.root, "ownership.json"), `${JSON.stringify(ownership(run, listeners), null, 2)}\n`); run.listeners = listeners; } export async function createOwnedRun({ repositoryRoot, runId, nonce, now, pid } = {}) { const repo = canonicalRoot(repositoryRoot); const base = canonicalIntegrationBase(repo); validateNoSymlinkAncestors(repo, base); await mkdir(join(repo, ".artifacts"), { mode: 0o700 }).catch((error) => { if (error.code !== "EEXIST") throw error; }); await mkdir(base, { mode: 0o700 }).catch((error) => { if (error.code !== "EEXIST") throw error; }); validateNoSymlinkAncestors(repo, base); const id = runId ?? `p1-${randomBytes(16).toString("hex")}`; const root = validateRunRoot(repo, join(base, id), id); const run = { repositoryRoot: repo, root, runId: id, nonce: nonce ?? randomBytes(32).toString("hex"), startedAt: now ?? nowIso(), pid: pid ?? process.pid, listeners: initialListeners(pid ?? process.pid), }; if (!HEX64.test(run.nonce) || !ISO_UTC.test(run.startedAt)) throw new Error("invalid ownership identity"); await mkdir(root, { mode: 0o700 }); await writeOwnership(run); return run; } function strictOwnership(value, run, expectedNonce) { if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("ownership is malformed"); const validListeners = Array.isArray(value.listeners) && value.listeners.length === 2 && value.listeners.every((listener, index) => { const expectedName = ["primary", "contextual"][index]; const common = listener?.name === expectedName && listener.kind === "fastify" && listener.host === "127.0.0.1" && listener.requestedPort === 0 && listener.pid === process.pid && ["not_started", "listening", "closed", "close_failed"].includes(listener.state); return common && (listener.state === "not_started" ? !("actualPort" in listener) : Number.isInteger(listener.actualPort) && listener.actualPort >= 1 && listener.actualPort <= 65535); }); if (value.schemaVersion !== 1 || value.runId !== run.runId || value.runNonce !== expectedNonce || value.root !== run.root || value.repositoryRoot !== run.repositoryRoot || value.pid !== process.pid || !ISO_UTC.test(value.startedAt ?? "") || !validListeners || JSON.stringify(value.resources) !== JSON.stringify(exactOwnedResources(run))) throw new Error("ownership identity mismatch"); return value; } export async function readAndValidateOwnership({ repositoryRoot, runRoot, expectedNonce }) { const repo = canonicalRoot(repositoryRoot); const id = basename(resolve(runRoot)); const lexical = validateRunRoot(repo, runRoot, id); const rootEntry = await lstat(lexical); if (!rootEntry.isDirectory() || rootEntry.isSymbolicLink()) throw new Error("owned run root is not a directory"); if (await realpath(lexical) !== lexical) throw new Error("owned run root is not canonical"); const ownershipPath = join(lexical, "ownership.json"); const ownershipEntry = await lstat(ownershipPath); if (!ownershipEntry.isFile() || ownershipEntry.isSymbolicLink()) throw new Error("ownership file is unsafe"); let value; try { value = JSON.parse(await readFile(ownershipPath, "utf8")); } catch { throw new Error("ownership is malformed"); } return strictOwnership(value, { repositoryRoot: repo, root: lexical, runId: id, nonce: expectedNonce, startedAt: value.startedAt, pid: process.pid, }, expectedNonce); } export async function cleanupOwnedRun({ repositoryRoot, runRoot, expectedNonce }) { const value = await readAndValidateOwnership({ repositoryRoot, runRoot, expectedNonce }); const base = canonicalIntegrationBase(repositoryRoot); const tombstone = join(base, `.deleting-${value.runId}-${expectedNonce.slice(0, 16)}`); await rename(runRoot, tombstone); await rm(tombstone, { recursive: true }); } export async function finalizeOwnedRun({ run, success, keep }) { if (!success || keep) return false; await cleanupOwnedRun({ repositoryRoot: run.repositoryRoot, runRoot: run.root, expectedNonce: run.nonce }); return true; } function resolveTrustedSystemExecutableSync(name) { const candidates = process.platform === "win32" ? [] : [`/usr/bin/${name}`, `/bin/${name}`, `/opt/homebrew/bin/${name}`, `/usr/local/bin/${name}`]; for (const candidate of candidates) { try { accessSync(candidate, fsConstants.X_OK); const canonical = realpathSync(candidate); if (statSync(canonical).isFile()) return canonical; } catch { /* try the next fixed trusted executable location */ } } throw new Error(`cannot resolve trusted system executable: ${name}`); } const THT_EDITABLE_FINDER_NORMALIZED_SHA256 = "3489b09b63511e27e1ae4d3f858d2bc576fe77beb5ea97607673781a32d2723e"; function assertRegularNonSymlink(path, label) { let entry; try { entry = lstatSync(path); } catch { throw new Error(`${label} is unavailable`); } if (!entry.isFile() || entry.isSymbolicLink() || realpathSync(path) !== path) throw new Error(`${label} identity is invalid`); return entry; } function assertSafeSitePackages(identity) { const entries = readdirSync(identity.sitePackages, { withFileTypes: true }); const pthNames = entries.filter(({ name }) => name.endsWith(".pth")).map(({ name }) => name); const finderNames = entries.filter(({ name }) => /^__editable___tht_.*_finder\.py$/.test(name)).map(({ name }) => name); if (pthNames.length !== 1 || pthNames[0] !== identity.pthName || finderNames.length !== 1 || finderNames[0] !== identity.finderName || entries.some((entry) => entry.isSymbolicLink())) { throw new Error("trusted THT editable binding is ambiguous"); } const startup = /^(?:sitecustomize|usercustomize|tht)(?:\..*)?$/; if (entries.some(({ name }) => startup.test(name))) throw new Error("trusted THT editable binding has an import startup override"); const cache = join(identity.sitePackages, "__pycache__"); if (existsSync(cache) && readdirSync(cache).some((name) => startup.test(name) || /^__editable___tht_.*_finder\..*\.pyc$/.test(name))) { throw new Error("trusted THT editable binding has an import startup override"); } assertRegularNonSymlink(identity.pthPath, "trusted THT editable pth"); assertRegularNonSymlink(identity.finderPath, "trusted THT editable finder"); const pth = readFileSync(identity.pthPath, "utf8"); const rawFinder = readFileSync(identity.finderPath, "utf8"); const finder = rawFinder.replaceAll("\r\n", "\n"); const occurrences = finder.split(identity.sourceRoot).length - 1; const normalized = finder.replaceAll(identity.sourceRoot, ""); if (pth !== identity.expectedPth || occurrences !== 5 || sha256(normalized) !== THT_EDITABLE_FINDER_NORMALIZED_SHA256) { throw new Error("trusted THT editable binding is invalid"); } return { pth, finder: rawFinder }; } function sourceTreeFilesSync(root, current = root, files = []) { for (const entry of readdirSync(current, { withFileTypes: true })) { const path = join(current, entry.name); if (entry.isSymbolicLink()) throw new Error("trusted THT source contains a symlink"); if (entry.isDirectory()) sourceTreeFilesSync(root, path, files); else if (entry.isFile()) files.push(relative(root, path).split(sep).join("/")); else throw new Error("trusted THT source contains a special file"); } return files; } function assertBoundThtFiles(identity) { assertRegularNonSymlink(identity.entrypointPath, "trusted THT entrypoint"); assertRegularNonSymlink(identity.pythonCanonicalPath, "trusted THT interpreter"); if (realpathSync(identity.pythonPath) !== identity.pythonCanonicalPath) throw new Error("trusted THT identity changed: interpreter"); if (sha256(readFileSync(identity.entrypointPath)) !== identity.entrypointSha256 || sha256(readFileSync(identity.pythonCanonicalPath)) !== identity.pythonSha256) { throw new Error("trusted THT identity changed: entrypoint or interpreter"); } for (const root of [dirname(identity.sourceRoot), dirname(identity.entrypointPath)]) { for (const name of ["sitecustomize.py", "sitecustomize.pyc", "usercustomize.py", "usercustomize.pyc", "tht.py", "tht.pyc"]) { if (existsSync(join(root, name))) throw new Error("trusted THT identity changed: import startup override"); } } const { pth, finder } = assertSafeSitePackages(identity); if (sha256(pth) !== identity.pthSha256 || sha256(finder) !== identity.editableFinderSha256) { throw new Error("trusted THT identity changed: editable binding"); } const actualPaths = sourceTreeFilesSync(identity.sourceRoot).map((path) => `harness/tht/${path}`); actualPaths.push("harness/pyproject.toml"); actualPaths.sort(); const expectedPaths = identity.sourceManifest.map(({ path }) => path).sort(); if (JSON.stringify(actualPaths) !== JSON.stringify(expectedPaths)) throw new Error("trusted THT identity changed: source manifest"); for (const file of identity.sourceManifest) { const path = join(identity.repositoryRoot, ...file.path.split("/")); assertRegularNonSymlink(path, "trusted THT source file"); if (sha256(readFileSync(path)) !== file.sha256) throw new Error("trusted THT identity changed: source bytes"); } } export function revalidateThtIdentity(identity) { try { assertBoundThtFiles(identity); } catch (error) { if (/^trusted THT identity changed/.test(error.message)) throw error; throw new Error(`trusted THT identity changed: ${error.message}`); } return true; } async function gitTrackedSourceManifest(repo, gitPath) { const listing = await runCommand({ executable: gitPath, argv: ["-C", repo, "ls-files", "-s", "-z", "--", "harness/tht", "harness/pyproject.toml"], env: baseSafeGitEnvironment(gitPath), }); const treeListing = await runCommand({ executable: gitPath, argv: ["-C", repo, "ls-tree", "-r", "-z", "HEAD", "--", "harness/tht", "harness/pyproject.toml"], env: baseSafeGitEnvironment(gitPath), }); const treeEntries = new Map(treeListing.stdout.split("\0").filter(Boolean).map((record) => { const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t(harness\/(?:tht\/[^\0]+|pyproject\.toml))$/.exec(record); if (!match) throw new Error("trusted THT Git tree identity is invalid"); return [match[3], { mode: match[1], oid: match[2] }]; })); const manifest = []; for (const record of listing.stdout.split("\0").filter(Boolean)) { const match = /^(100644|100755) ([0-9a-f]{40,64}) 0\t(harness\/(?:tht\/[^\0]+|pyproject\.toml))$/.exec(record); if (!match) throw new Error("trusted THT Git index identity is invalid"); const [, mode, oid, path] = match; const tree = treeEntries.get(path); if (!tree || tree.mode !== mode || tree.oid !== oid) throw new Error(`trusted THT Git index differs from HEAD tree: ${path}`); treeEntries.delete(path); const blob = await runCommand({ executable: gitPath, argv: ["-C", repo, "cat-file", "blob", oid], env: baseSafeGitEnvironment(gitPath) }); const bytes = Buffer.from(blob.stdout); const worktreePath = join(repo, ...path.split("/")); assertRegularNonSymlink(worktreePath, "trusted THT source file"); if (!bytes.equals(readFileSync(worktreePath))) throw new Error(`trusted THT source bytes differ from Git: ${path}`); manifest.push({ path, mode, gitBlob: oid, sha256: sha256(bytes) }); } if (treeEntries.size !== 0) throw new Error("trusted THT Git index differs from HEAD tree"); manifest.sort((left, right) => left.path.localeCompare(right.path)); if (!manifest.some(({ path }) => path === "harness/pyproject.toml") || !manifest.some(({ path }) => path === "harness/tht/cli/__init__.py")) throw new Error("trusted THT tracked source manifest is incomplete"); return manifest; } export async function resolveProductionExecutables({ repositoryRoot } = {}) { const repo = canonicalRoot(repositoryRoot); const gitPath = resolveTrustedSystemExecutableSync("git"); const pythonPath = resolveTrustedSystemExecutableSync("python3"); const thtPath = join(repo, "harness", ".venv", "bin", "tht"); assertRegularNonSymlink(thtPath, "trusted THT entrypoint"); 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 pythonVersion = basename(pythonLexical); const sitePackages = join(repo, "harness", ".venv", "lib", pythonVersion, "site-packages"); const siteEntries = await readdir(sitePackages); const pthNames = siteEntries.filter((name) => name.endsWith(".pth")); const finderNames = siteEntries.filter((name) => /^__editable___tht_.*_finder\.py$/.test(name)); if (pthNames.length !== 1 || finderNames.length !== 1) throw new Error("trusted THT editable binding is ambiguous"); const finderModule = finderNames[0].slice(0, -3); const identity = { repositoryRoot: repo, entrypoint: "generated-console-script", entrypointPath: thtPath, entrypointSha256: sha256(entrypointBytes), pythonPath: pythonLexical, pythonCanonicalPath: realpathSync(pythonLexical), pythonSha256: sha256(await readFile(realpathSync(pythonLexical))), sourceRoot, sourceStatus: "git-index-byte-identical", sitePackages, pthName: pthNames[0], pthPath: join(sitePackages, pthNames[0]), expectedPth: `import ${finderModule}; ${finderModule}.install()`, finderName: finderNames[0], finderPath: join(sitePackages, finderNames[0]), }; const binding = assertSafeSitePackages(identity); identity.pthSha256 = sha256(binding.pth); identity.editableFinderSha256 = sha256(binding.finder); identity.sourceManifest = await gitTrackedSourceManifest(repo, gitPath); assertBoundThtFiles(identity); return { gitPath, pythonPath, thtPath, thtIdentity: identity }; } let fallbackGitHooksPath; function ownedFallbackGitHooksPath() { if (!fallbackGitHooksPath) fallbackGitHooksPath = realpathSync(mkdtempSync(join(tmpdir(), `p1-git-hooks-${process.pid}-`))); return fallbackGitHooksPath; } function gitSafeConfig(hooksPath) { return [ ["core.hooksPath", hooksPath], ["core.attributesFile", "/dev/null"], ["core.fsmonitor", "false"], ["core.pager", "/bin/cat"], ["pager.status", "false"], ["diff.external", ""], ["interactive.diffFilter", ""], ["commit.gpgSign", "false"], ["tag.gpgSign", "false"], ["user.signingKey", ""], ["gpg.program", "/bin/false"], ["credential.helper", ""], ["core.askPass", "/bin/false"], ["sequence.editor", "/bin/false"], ["core.editor", "/bin/false"], ["protocol.allow", "never"], ["protocol.file.allow", "always"], ["protocol.ext.allow", "never"], ]; } function hardenedGitArgv(argv, hooksPath) { return [...gitSafeConfig(hooksPath).flatMap(([key, value]) => ["-c", `${key}=${value}`]), ...argv]; } function baseSafeGitEnvironment(gitPath) { return { PATH: dirname(gitPath), HOME: "/nonexistent", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_SYSTEM: "/dev/null", GIT_CONFIG_GLOBAL: "/dev/null", GIT_ATTR_NOSYSTEM: "1", GIT_TERMINAL_PROMPT: "0", GIT_ASKPASS: "/bin/false", SSH_ASKPASS: "/bin/false", GIT_ALLOW_PROTOCOL: "file", GIT_PROTOCOL_FROM_USER: "0", GIT_PAGER: "/bin/cat", }; } function assertEmptyHooksDirectory(path) { let entry; try { entry = lstatSync(path); } catch { throw new Error("unsafe Git repository state: hooks directory is unavailable"); } if (!entry.isDirectory() || entry.isSymbolicLink() || realpathSync(path) !== path || readdirSync(path).length !== 0) { throw new Error("unsafe Git repository state: hooks directory is not owned and empty"); } } function parseLocalGitConfig(bytes) { let section; const entries = []; for (const raw of bytes.replaceAll("\r\n", "\n").split("\n")) { const line = raw.trim(); if (!line || line.startsWith("#") || line.startsWith(";")) continue; const sectionMatch = /^\[([A-Za-z0-9.-]+)(?:\s+"([^"\\]*)")?\]$/.exec(line); if (sectionMatch) { section = sectionMatch[2] ? `${sectionMatch[1].toLowerCase()}.${sectionMatch[2]}` : sectionMatch[1].toLowerCase(); continue; } const valueMatch = /^([A-Za-z0-9.-]+)\s*=\s*(.*)$/.exec(line); if (!section || !valueMatch || /[\\\0]/.test(valueMatch[2])) throw new Error("unsafe Git repository state: local config is malformed"); entries.push([`${section}.${valueMatch[1].toLowerCase()}`, valueMatch[2]]); } return entries; } function safeLocalGitConfigEntry(key, value, runRoot) { if (key === "core.repositoryformatversion") return value === "0"; if (["core.filemode", "core.bare", "core.logallrefupdates", "core.ignorecase", "core.precomposeunicode"].includes(key)) return /^(?:true|false)$/.test(value); if (key === "remote.origin.url") return ownedGitPath(value, runRoot); if (key === "remote.origin.fetch") return /^\+refs\/heads\/(?:\*|main|invalid-context):refs\/remotes\/origin\/(?:\*|main|invalid-context)$/.test(value); if (/^branch\.(?:main|invalid-context)\.remote$/.test(key)) return value === "origin"; if (/^branch\.(?:main|invalid-context)\.merge$/.test(key)) return /^refs\/heads\/(?:main|invalid-context)$/.test(value); if (key === "user.name") return FIXTURE_GIT_CONFIG.get("user.name")?.has(value) === true; if (key === "user.email") return FIXTURE_GIT_CONFIG.get("user.email")?.has(value) === true; return false; } function repositoryGitDirectory(argv, cwd, runRoot) { let candidate; if (argv[0] === "--git-dir") candidate = argv[1]; else if (argv[0] === "-C") candidate = join(argv[1], ".git"); else if (cwd) candidate = join(cwd, ".git"); if (!candidate || !ownedGitPath(resolve(candidate), runRoot) || !existsSync(candidate)) return undefined; const entry = lstatSync(candidate); if (entry.isFile() && !entry.isSymbolicLink()) { const match = /^gitdir: (.+)\n?$/.exec(readFileSync(candidate, "utf8")); if (!match) throw new Error("unsafe Git repository state: gitdir file is malformed"); candidate = resolve(dirname(candidate), match[1]); } else if (!entry.isDirectory() || entry.isSymbolicLink()) throw new Error("unsafe Git repository state: git directory is unsafe"); return realpathSync(candidate); } function findAttributes(current, gitDirectory, findings = []) { for (const entry of readdirSync(current, { withFileTypes: true })) { const path = join(current, entry.name); if (path === gitDirectory || (entry.name === ".git" && (entry.isDirectory() || entry.isFile()))) continue; if (entry.isSymbolicLink()) throw new Error("unsafe Git repository state: worktree contains a symlink"); if (entry.isDirectory()) findAttributes(path, gitDirectory, findings); else if (entry.name === ".gitattributes") findings.push(path); } return findings; } function validateGitDirectoryState(gitDirectory, runRoot) { const configPath = join(gitDirectory, "config"); const configEntry = lstatSync(configPath); if (!configEntry.isFile() || configEntry.isSymbolicLink()) throw new Error("unsafe Git repository state: local config is unsafe"); const entries = parseLocalGitConfig(readFileSync(configPath, "utf8")); if (entries.some(([key, value]) => !safeLocalGitConfigEntry(key, value, runRoot))) { throw new Error("unsafe Git repository state: local config is not exact"); } const infoAttributes = join(gitDirectory, "info", "attributes"); if (existsSync(infoAttributes)) { const entry = lstatSync(infoAttributes); if (!entry.isFile() || entry.isSymbolicLink() || readFileSync(infoAttributes).length !== 0) { throw new Error("unsafe Git repository state: info attributes are not empty"); } } const hooks = join(gitDirectory, "hooks"); if (existsSync(hooks)) for (const entry of readdirSync(hooks, { withFileTypes: true })) { if (entry.isSymbolicLink() || !entry.isFile() || !entry.name.endsWith(".sample")) { throw new Error("unsafe Git repository state: repository hook is present"); } } const worktree = dirname(gitDirectory); if (basename(gitDirectory) === ".git" && findAttributes(worktree, gitDirectory).length > 0) { throw new Error("unsafe Git repository state: worktree attributes are present"); } return entries; } function exactRemoteTarget(argv, entries) { const offset = argv[0] === "-c" || argv[0] === "-C" || argv[0] === "--git-dir" ? 2 : 0; const verb = argv[offset]; const args = argv.slice(offset + 1); if (verb === "clone") { const operands = args.filter((value) => value !== "--bare" && value !== "--single-branch" && value !== "--" && value !== "--branch" && value !== "main" && value !== "invalid-context"); return operands.at(-2); } if (["fetch", "push"].includes(verb) && args.includes("origin")) { return entries.find(([key]) => key === "remote.origin.url")?.[1]; } return undefined; } function assertSafeGitRepositoryState(argv, cwd, runRoot, fixedHooksPath) { assertEmptyHooksDirectory(fixedHooksPath); if (argv[0] === "-c") assertEmptyHooksDirectory(argv[1].slice("core.hooksPath=".length)); const gitDirectory = repositoryGitDirectory(argv, cwd, runRoot); const entries = gitDirectory ? validateGitDirectoryState(gitDirectory, runRoot) : []; const target = exactRemoteTarget(argv, entries); if (target !== undefined) { if (!ownedGitPath(target, runRoot) || !existsSync(join(target, "config"))) { throw new Error("unsafe Git repository state: remote target is not exact and owned"); } validateGitDirectoryState(realpathSync(target), runRoot); } } function rawGitCommonDirectory(gitDirectory) { const path = join(gitDirectory, "commondir"); if (!existsSync(path)) return gitDirectory; const entry = lstatSync(path); const value = entry.isFile() && !entry.isSymbolicLink() ? readFileSync(path, "utf8") : ""; if (!/^[^\0\n\r]+\n?$/.test(value)) throw new Error("unsafe Git repository state: common directory is unsafe"); const common = resolve(gitDirectory, value.trimEnd()); const commonEntry = lstatSync(common); if (!commonEntry.isDirectory() || commonEntry.isSymbolicLink() || realpathSync(common) !== common) { throw new Error("unsafe Git repository state: common directory is unsafe"); } return common; } function rawGitConfigEntries(path, required = false) { if (!existsSync(path)) { if (required) throw new Error("unsafe Git repository state: local config is unavailable"); return []; } const entry = lstatSync(path); if (!entry.isFile() || entry.isSymbolicLink()) throw new Error("unsafe Git repository state: local config is unsafe"); return parseLocalGitConfig(readFileSync(path, "utf8")); } function unsafeRawGitConfigKey(key, value = "") { const normalized = key.toLowerCase(); if (normalized === "core.fsmonitor" && /^(?:true|false|1|0)$/i.test(String(value).trim())) return false; return /^(?:filter|diff)\..+\.(?:clean|smudge|process|required|textconv|external|command)$/.test(normalized) || /^(?:remote\..+\.(?:uploadpack|receivepack)|uploadpack\..+|receivepack\..+)$/.test(normalized) || /^credential(?:\..+)?\.helper$/.test(normalized) || /^(?:core\.(?:hookspath|attributesfile|fsmonitor|sshcommand|askpass|pager|editor|sequence\.editor)|sequence\.editor|interactive\.difffilter|diff\.external|gpg\.program|gpg\.ssh\.program)$/.test(normalized) || /^merge\..+\.driver$/.test(normalized) || /^(?:pager\..+|alias\..+|difftool\..+\.(?:cmd|path)|mergetool\..+\.(?:cmd|path))$/.test(normalized) || /^include(?:if\..+)?\.path$/.test(normalized); } function validateRawGitDirectoryState(gitDirectory) { const common = rawGitCommonDirectory(gitDirectory); const entries = [ ...rawGitConfigEntries(join(common, "config"), true), ...rawGitConfigEntries(join(gitDirectory, "config.worktree")), ]; if (entries.some(([key, value]) => unsafeRawGitConfigKey(key, value))) { throw new Error("unsafe Git repository state: executable local config is present"); } for (const hooks of new Set([join(common, "hooks"), join(gitDirectory, "hooks")])) { if (!existsSync(hooks)) continue; const hooksEntry = lstatSync(hooks); if (!hooksEntry.isDirectory() || hooksEntry.isSymbolicLink()) throw new Error("unsafe Git repository state: repository hooks are unsafe"); for (const entry of readdirSync(hooks, { withFileTypes: true })) { if (entry.isSymbolicLink() || !entry.isFile() || !entry.name.endsWith(".sample")) { throw new Error("unsafe Git repository state: repository hook is present"); } } } return entries; } function rawRepositoryGitDirectory(argv, cwd) { if (argv[0] === "--git-dir") return repositoryGitDirectory(argv, cwd); let current = argv[0] === "-C" ? argv[1] : cwd; if (!current) return undefined; current = realpathSync(current); while (true) { if (existsSync(join(current, ".git"))) return repositoryGitDirectory(["-C", current]); const parent = dirname(current); if (parent === current) return undefined; current = parent; } } function assertSafeRawGitRepositoryState(argv, cwd, fixedHooksPath) { assertEmptyHooksDirectory(fixedHooksPath); const gitDirectory = rawRepositoryGitDirectory(argv, cwd); const entries = gitDirectory ? validateRawGitDirectoryState(gitDirectory) : []; const target = exactRemoteTarget(argv, entries); if (target !== undefined) { if (!ownedGitPath(target) || !existsSync(join(target, "config"))) { throw new Error("unsafe Git repository state: remote target is not exact and owned"); } validateRawGitDirectoryState(realpathSync(target)); } } 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 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 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"]) || exactArray(args, ["--porcelain=v1", "--untracked-files=all", "--", "harness/tht", "harness/pyproject.toml"]))); break; case "ls-files": valid = prefix === "-C" && exactArray(args, ["-s", "-z", "--", "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"])) || (prefix === "-C" && exactArray(args, ["-r", "-z", "HEAD", "--", "harness/tht", "harness/pyproject.toml"])); 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" || prefix === "-C") && 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) { const environment = value ?? process.env; if (!environment || typeof environment !== "object" || Array.isArray(environment)) throw new Error("child environment is invalid"); const allowedExtra = new Set([ "GIT_AUTHOR_NAME", "GIT_AUTHOR_EMAIL", "GIT_COMMITTER_NAME", "GIT_COMMITTER_EMAIL", "THT_AUTH_USER_ID", "THT_AUTH_USERNAME", "THT_AUTH_IS_ADMIN", "THT_DWH_API_KEY", "THT_VEC_API_KEY", "THT_VEC_WRITE_API_KEY", "THT_CA", "THT_SSL_CA", ]); for (const [key, value] of Object.entries(environment)) { if (typeof value !== "string" || (!(key in expected) && !allowedExtra.has(key))) throw new Error("child environment exceeds acceptance bounds"); } 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: 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, 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 !== "git-index-byte-identical") throw new Error("trusted THT identity is absent"); const gitHooksPath = join(runRoot, "installation", "runtime", "acceptance-git-hooks"); mkdirSync(gitHooksPath, { recursive: true, mode: 0o700 }); assertEmptyHooksDirectory(gitHooksPath); if (failPatchAt !== undefined && (!Number.isInteger(failPatchAt) || failPatchAt < 1 || failPatchAt > 12)) throw new Error("invalid production patch failure probe"); const token = Symbol("p1-production-surface"); const events = []; const originals = { execFile: mutableChildProcess.execFile, spawn: mutableChildProcess.spawn, exec: mutableChildProcess.exec, execSync: mutableChildProcess.execSync, execFileSync: mutableChildProcess.execFileSync, spawnSync: mutableChildProcess.spawnSync, fork: mutableChildProcess.fork, createSocket: mutableDgram.createSocket, Worker: mutableWorkerThreads.Worker, dns: new Map(), dnsPromises: new Map(), dlopen: process.dlopen, }; 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) { validateGitInvocation(argv, { runRoot }); if (options.cwd !== undefined && !ownedGitPath(options.cwd, runRoot)) throw new Error("Git working directory is prohibited"); assertSafeGitRepositoryState(argv, options.cwd, runRoot, gitHooksPath); const logical = argv[0] === "-c" ? argv.slice(2) : argv; return { executable: gitPath, kind: "git", argv: hardenedGitArgv(logical, gitHooksPath) }; } if (canonical === thtPath) { validateThtInvocation(argv, { thtPath, runRoot, cwd: options.cwd }); revalidateThtIdentity(thtIdentity); return { executable: thtPath, kind: "tht", argv }; } if (canonical === pythonPath) { 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"); } return { executable: pythonPath, kind: "python-lock-holder", argv }; } 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 { 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, resolved.argv, bounded, (error, stdout, stderr) => { safeChildEvent(events, { api: "execFile", executable: resolved.executable, argv, outcome: error ? "FAIL" : "PASS", detail: resolved.kind }); callback?.(error, stdout, stderr); }); }; Object.defineProperty(guardedExecFile, promisify.custom, { value: (executable, argv, options) => new Promise((resolvePromise, reject) => { guardedExecFile(executable, argv, options, (error, stdout, stderr) => error ? reject(Object.assign(error, { stdout, stderr })) : resolvePromise({ stdout, stderr })); }) }); const guardedSpawn = function guardedSpawn(executable, argv, options = {}) { if (!Array.isArray(argv)) return rejectChild("spawn", [executable]); let resolved; 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" } }); const child = originals.spawn(resolved.executable, resolved.argv, bounded); let bytes = 0; const count = (chunk) => { bytes += chunk.length; if (bytes > MAX_OUTPUT) child.kill("SIGKILL"); }; child.stdout?.on("data", count); child.stderr?.on("data", count); const timer = setTimeout(() => child.kill("SIGKILL"), 300_000); timer.unref(); child.once("exit", (code) => { clearTimeout(timer); safeChildEvent(events, { api: "spawn", executable: resolved.executable, argv, outcome: code === 0 ? "PASS" : "FAIL", detail: resolved.kind }); }); child.once("error", () => { clearTimeout(timer); }); return child; }; 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"); } } 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; 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: "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); 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 value(...args); } 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"); }; 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, gitPolicy: { hooksPath: gitHooksPath, fixedConfig: gitSafeConfig(gitHooksPath) }, addOwnedOrigin: network.addOwnedOrigin, hasOwnedOrigin: network.hasOwnedOrigin, restore() { if (restored) throw new Error("production surface guard restored twice"); restored = true; 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) { 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)) 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)) policyError("command executable is invalid", details()); let canonical; 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) 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()); const guardedByProduction = productionSurfaceOwner !== undefined; let rawGitHooksPath; let rawGitArgv; try { if (canonical === allowedGit) { validateGitInvocation(argv, { runRoot: activeExecutablePolicy?.runRoot }); if (!guardedByProduction) { rawGitHooksPath = ownedFallbackGitHooksPath(); rawGitArgv = argv[0] === "-c" ? argv.slice(2) : argv; assertSafeRawGitRepositoryState(rawGitArgv, cwd, rawGitHooksPath); } } 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 childArgv = canonical === allowedGit && !guardedByProduction ? hardenedGitArgv(rawGitArgv, rawGitHooksPath) : argv; const childEnv = canonical === allowedGit && !guardedByProduction ? { ...(env ?? {}), ...baseSafeGitEnvironment(canonical) } : env; const child = mutableChildProcess.execFile(canonical, childArgv, { cwd, env: childEnv, 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: sanitizedArgvLabels(argv), outcome: error ? "FAIL" : "PASS", ...(activeCommandCheckId ? { checkId: activeCommandCheckId } : {}), }); if (error) Object.assign(error, { result }); error ? reject(error) : resolvePromise(result); }); if (stdin !== undefined) { child.stdin.end(stdin); } }); } async function git(argv, options = {}) { return await runCommand({ executable: activeExecutablePolicy?.gitPath ?? resolveTrustedSystemExecutableSync("git"), argv, ...options }); } async function tht(executable, argv, options = {}) { return await runCommand({ executable, argv, ...options }); } function safeArtifactPath(path) { if (typeof path !== "string" || !SAFE_RELATIVE.test(path) || path.startsWith(".") || path.includes("//")) throw new Error("unsafe artifact path"); return path; } async function fileArtifact(runRoot, path) { safeArtifactPath(path); return { path, sha256: sha256(await readFile(join(runRoot, path))) }; } function forbiddenKey(value) { if (!value || typeof value !== "object") return false; if (Array.isArray(value)) return value.some(forbiddenKey); for (const [key, nested] of Object.entries(value)) { if (/^(attempt|attempts|retry|retries)$/i.test(key) || forbiddenKey(nested)) return true; } return false; } export function deriveOverall(checks) { return checks.length > 0 && checks.every(({ status }) => status === "PASS") ? "PASS" : "FAIL"; } function hasExactCheckIds(checks) { return checks.length === CHECK_IDS.length && checks.every(({ id }, index) => id === CHECK_IDS[index]); } export function validateReport(report) { if (!report || report.schemaVersion !== 1 || !RUN_ID.test(report.runId ?? "") || !ISO_UTC.test(report.startedAt ?? "") || !ISO_UTC.test(report.finishedAt ?? "") || typeof report.command !== "string" || forbiddenKey(report) || !Array.isArray(report.checks) || !hasExactCheckIds(report.checks)) throw new Error("report is invalid"); const ids = new Set(); const artifactPaths = new Set(); for (const check of report.checks) { if (!check || !/^[a-z0-9_]+$/.test(check.id ?? "") || ids.has(check.id) || !["PASS", "FAIL"].includes(check.status) || !ISO_UTC.test(check.startedAt ?? "") || !ISO_UTC.test(check.finishedAt ?? "") || !Array.isArray(check.commands) || check.commands.some((name) => !COMMAND.test(name)) || !Array.isArray(check.artifacts) || check.artifacts.some(({ path, sha256 }) => { try { safeArtifactPath(path); } catch { return true; } return !HEX64.test(sha256 ?? ""); })) throw new Error("report check is invalid"); ids.add(check.id); for (const artifact of check.artifacts) { if (artifactPaths.has(artifact.path)) throw new Error("report artifact path is duplicated"); artifactPaths.add(artifact.path); } } if (report.overall !== deriveOverall(report.checks)) throw new Error("report overall is not derived"); return report; } function renderReportMarkdown(report) { validateReport(report); const rows = report.checks.map((check) => `| ${check.id} | ${check.status} |`).join("\n"); return `# P1 automated integration\n\nRun: \`${report.runId}\`\n\n| Check | Status |\n|---|---|\n${rows}\n\nautomated integration: ${report.overall}\nmanual acceptance: PENDING\n`; } function containsAny(bytes, forbiddenValues) { return forbiddenValues.some((value) => value && bytes.includes(Buffer.from(value))); } async function walkFiles(root, current = root, out = []) { for (const entry of await readdir(current, { withFileTypes: true })) { const path = join(current, entry.name); const rel = relative(root, path).split(sep).join("/"); if (entry.isSymbolicLink()) throw new Error(`secret scan failed closed: symlink outside fixture-secrets: ${rel}`); if (entry.isDirectory()) { if (rel === "fixture-secrets") continue; await walkFiles(root, path, out); } else if (entry.isFile()) out.push({ path, rel }); } return out; } async function gitObjectScan(runRoot, forbiddenValues, expectedGitRepositories) { const findings = []; const repositories = []; for (const rel of expectedGitRepositories) { const directory = join(runRoot, rel); if (!existsSync(directory)) throw new Error(`Git secret scan failed closed: missing expected Git repository: ${rel}`); const args = basename(directory) === "remote.git" ? ["--git-dir", directory] : ["-C", directory]; let objects; try { objects = (await git([...args, "rev-list", "--objects", "--all"])).stdout.trim().split("\n").filter(Boolean); } catch { throw new Error(`Git secret scan failed closed during enumeration: ${basename(directory)}`); } let blobCount = 0; for (const line of objects) { const oid = line.split(" ", 1)[0]; let type; let bytes; try { type = (await git([...args, "cat-file", "-t", oid])).stdout.trim(); if (!/^(blob|tree|commit|tag)$/.test(type)) throw new Error("invalid object type"); if (type !== "blob") continue; blobCount += 1; bytes = Buffer.from((await git([...args, "cat-file", "blob", oid], { maxOutputBytes: 16 * 1024 * 1024 })).stdout); } catch { throw new Error(`Git secret scan failed closed during object inspection: ${basename(directory)}:${oid}`); } if (containsAny(bytes, forbiddenValues)) findings.push({ path: `git-object:${rel}:${oid}` }); } repositories.push({ path: rel, objectCount: objects.length, blobCount }); } return { findings, repositories }; } export async function scanSecretsDetailed({ runRoot, forbiddenValues, virtualFiles = [], expectedGitRepositories = ["remote.git", "author"] }) { const values = forbiddenValues.filter((value) => typeof value === "string" && value.length >= 8); const findings = []; for (const file of await walkFiles(runRoot)) if (containsAny(await readFile(file.path), values)) findings.push({ path: file.rel }); for (const file of virtualFiles) if (containsAny(Buffer.from(file.bytes), values)) findings.push({ path: file.path }); const gitScan = await gitObjectScan(runRoot, values, expectedGitRepositories); findings.push(...gitScan.findings); return { findings, repositories: gitScan.repositories }; } export async function scanSecrets(options) { return (await scanSecretsDetailed(options)).findings; } export function negativeRequestEvidence(caseLabel, expectedInputField) { if (!/^[a-z0-9-]+$/.test(caseLabel) || !/^[a-z_]+(?:\.[a-z_]+)*$/.test(expectedInputField)) throw new Error("unsafe negative-case evidence"); return { case: caseLabel, expectedInputField }; } function loopbackOrigin(value) { const url = new URL(value); if (url.protocol !== "http:" || url.hostname !== "127.0.0.1" || !url.port) throw new Error("owned API must be loopback HTTP"); return url.origin; } export function installExternalFetchGuard(ownedBaseUrl, fetchImplementation = globalThis.fetch) { const ownedOrigin = loopbackOrigin(ownedBaseUrl); const externalAttempts = []; const guardedFetch = async (input, init) => { const candidate = new URL(typeof input === "string" || input instanceof URL ? input : input.url); if (candidate.origin !== ownedOrigin) { externalAttempts.push({ transport: "fetch", protocol: candidate.protocol, loopback: candidate.hostname === "127.0.0.1" }); throw new Error("external fetch prohibited"); } return await fetchImplementation(input, init); }; return { fetch: guardedFetch, externalAttempts }; } function socketDestination(args) { const first = Array.isArray(args[0]) ? args[0][0] : args[0]; if (typeof first === "object" && first !== null) { if (first.path !== undefined) return { path: String(first.path) }; return { host: String(first.host ?? first.hostname ?? "localhost"), port: Number(first.port) }; } if (typeof first === "number") return { host: typeof args[1] === "string" ? args[1] : "localhost", port: first }; return { path: String(first) }; } export function installNetworkGuard(fetchImplementation = globalThis.fetch) { if (typeof fetchImplementation !== "function") throw new Error("global fetch is unavailable"); const ownedOrigins = new Set(); const externalAttempts = []; const originalFetch = globalThis.fetch; const originalConnect = Socket.prototype.connect; const isOwned = (host, port) => { if (!Number.isInteger(port) || port < 1 || port > 65535) return false; const normalized = host === "localhost" || host === "::1" ? "127.0.0.1" : host; return isIP(normalized) !== 0 && normalized === "127.0.0.1" && ownedOrigins.has(`http://127.0.0.1:${port}`); }; const guardedFetch = async (input, init) => { const candidate = new URL(typeof input === "string" || input instanceof URL ? input : input.url); if (!ownedOrigins.has(candidate.origin)) { externalAttempts.push({ transport: "fetch", protocol: candidate.protocol, loopback: candidate.hostname === "127.0.0.1" }); throw new Error("external network connection prohibited"); } return await fetchImplementation(input, init); }; const guardedSocketConnect = function guardedSocketConnect(...args) { const destination = socketDestination(args); if (!("host" in destination) || !isOwned(destination.host, destination.port)) { externalAttempts.push({ transport: "socket", loopback: destination.host === "127.0.0.1" }); throw new Error("external network connection prohibited"); } return originalConnect.apply(this, args); }; 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, addOwnedOrigin(value) { ownedOrigins.add(loopbackOrigin(value)); }, hasOwnedOrigin(value) { return ownedOrigins.has(loopbackOrigin(value)); }, restore() { if (restored) throw new Error("network guard restored twice"); restored = true; const tampered = globalThis.fetch !== guardedFetch || Socket.prototype.connect !== guardedSocketConnect; globalThis.fetch = originalFetch; Socket.prototype.connect = originalConnect; if (tampered) throw new Error("network guard ownership changed"); }, }; } function sanitizeForEvidence(value, forbiddenValues = []) { if (typeof value === "string") { let safe = value; for (const forbidden of forbiddenValues) if (forbidden) safe = safe.split(forbidden).join("[REDACTED]"); return safe.length > 16_384 ? `${safe.slice(0, 16_384)}[TRUNCATED]` : safe; } if (Array.isArray(value)) return value.map((item) => sanitizeForEvidence(item, forbiddenValues)); if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, nested]) => [key, sanitizeForEvidence(nested, forbiddenValues)])); return value; } async function evidence(run, path, value, forbiddenValues = []) { const safe = sanitizeForEvidence(value, forbiddenValues); await atomicWrite(join(run.root, path), `${JSON.stringify(safe, null, 2)}\n`); return await fileArtifact(run.root, path); } export async function executeChecks({ checks, failAt, recorder } = {}) { if (!Array.isArray(checks) || !hasExactCheckIds(checks)) throw new Error("scenarios must match the exact ordered check set"); if (failAt !== undefined && !CHECK_IDS.includes(failAt)) throw new Error("failure hook must name an exact check"); const results = []; let stopped = false; for (const scenario of checks) { const startedAt = nowIso(); let result; if (stopped) { result = { id: scenario.id, status: "FAIL", startedAt, finishedAt: nowIso(), commands: [], artifacts: [], error: "Not executed after earlier failure." }; } else { try { const output = await scenario.run(); if (scenario.id === failAt) { const injected = new Error("injected acceptance failure"); injected.acceptancePartial = { commands: output.commands ?? [], artifacts: output.artifacts ?? [] }; throw injected; } result = { id: scenario.id, status: "PASS", startedAt, finishedAt: nowIso(), commands: output.commands ?? [], artifacts: output.artifacts ?? [] }; } catch (error) { const partial = error?.acceptancePartial ?? {}; result = { id: scenario.id, status: "FAIL", startedAt, finishedAt: nowIso(), commands: partial.commands ?? [], artifacts: partial.artifacts ?? [], error: "Acceptance scenario failed safely." }; stopped = true; } } results.push(result); if (recorder) await recorder(result); } return results; } function baseWorkspace(id, evidenceSource) { return { workspace: { schema_version: 4, id, name: `P1 ${id}`, language: "en" }, dwh: { engine: "postgres", database: "postgres", schema: "public", supported_transports: ["postgres_direct"] }, evidence: { source: evidenceSource, policy: { max_chunk_chars: 4000, retain_published_generations: 3 } }, }; } function descriptors() { return [ baseWorkspace("p1-filesystem", { type: "filesystem", uri: "workspace-content/p1-filesystem/evidence", patterns: ["**/*.md"], max_bytes: 10485760 }), baseWorkspace("p1-http", { type: "http", uris: ["https://evidence.example.test/guide.md"], authentication: "signed_urls_file", connect_timeout_ms: 1250, read_timeout_ms: 30001, max_bytes: 12345, max_redirects: 2, allow_private_hosts: false, max_cache_bytes: 67890 }), baseWorkspace("p1-s3", { type: "s3", uri: "s3://p1-evidence/published/", endpoint_url: "https://s3.example.test/", region: "eu-west-1", credentials: "static_files", trusted_endpoint: true, allow_private_endpoint: false, allow_insecure_endpoint: false, max_bytes: 12345, max_objects: 33, max_pages: 4, page_size: 5 }), ]; } function namespace(id) { return id.toUpperCase().replaceAll("-", "_"); } async function createTopology(run) { for (const path of TOPOLOGY) await mkdir(join(run.root, path), { recursive: true, mode: path === "fixture-secrets" ? 0o700 : 0o755 }); } async function setupSecrets(ctx) { const secretDir = join(ctx.run.root, "fixture-secrets"); const values = { dwh: `DWH-${randomBytes(16).toString("hex")}`, signed: `SIGNED-${randomBytes(16).toString("hex")}`, access: `ACCESS-${randomBytes(16).toString("hex")}`, secret: `SECRET-${randomBytes(16).toString("hex")}`, session: `SESSION-${randomBytes(16).toString("hex")}`, rejected: `REJECTED-${randomBytes(16).toString("hex")}`, }; ctx.forbiddenValues = Object.values(values); ctx.secretValues = values; const paths = { dwh: join(secretDir, "dwh-password"), signed: join(secretDir, "evidence-signed-urls.json"), access: join(secretDir, "evidence-access"), secret: join(secretDir, "evidence-secret"), session: join(secretDir, "evidence-session"), }; await atomicWrite(paths.dwh, scalarSecretBytes(values.dwh)); await atomicWrite(paths.signed, JSON.stringify([`https://evidence.example.test/guide.md?token=${values.signed}`])); await atomicWrite(paths.access, scalarSecretBytes(values.access)); await atomicWrite(paths.secret, scalarSecretBytes(values.secret)); await atomicWrite(paths.session, scalarSecretBytes(values.session)); const env = {}; for (const workspace of ctx.descriptors) { const ns = namespace(workspace.workspace.id); const prefix = `THT_WS_${ns}`; Object.assign(env, { [`${prefix}_DWH_TRANSPORT`]: "postgres_direct", [`${prefix}_DWH_HOST`]: "dwh.invalid", [`${prefix}_DWH_PORT`]: "5432", [`${prefix}_DWH_USER`]: "reader", [`${prefix}_DWH_PASSWORD_FILE`]: paths.dwh, }); } Object.assign(env, { THT_WS_P1_HTTP_EVIDENCE_SIGNED_URLS_FILE: paths.signed, THT_WS_P1_S3_EVIDENCE_ACCESS_KEY_FILE: paths.access, THT_WS_P1_S3_EVIDENCE_SECRET_KEY_FILE: paths.secret, THT_WS_P1_S3_EVIDENCE_SESSION_TOKEN_FILE: paths.session, }); Object.assign(ctx.env, env); env.THT_WORKSPACE_SECRET_ROOTS = secretDir; await atomicWrite(join(ctx.run.root, "installation", "bindings.env"), `${Object.entries(env).map(([key, value]) => `${key}=${value}`).join("\n")}\n`); await atomicWrite(join(ctx.run.root, "installation", "runtime", "base.yaml"), "{}\n"); } async function initializeGit(ctx) { await git(["init", "--bare", "--initial-branch=main", join(ctx.run.root, "remote.git")], { cwd: ctx.run.root }); await git(["clone", join(ctx.run.root, "remote.git"), join(ctx.run.root, "author")], { cwd: ctx.run.root }); await git(["config", "user.name", "P1 Fixture Curator"], { cwd: join(ctx.run.root, "author") }); await git(["config", "user.email", "p1-curator@example.invalid"], { cwd: join(ctx.run.root, "author") }); const evidenceRoot = join(ctx.run.root, "author", "workspace-content", "p1-filesystem", "evidence"); await mkdir(join(evidenceRoot, "domain"), { recursive: true }); await writeFile(join(evidenceRoot, "guide.md"), "# P1 curated Evidence\n"); await writeFile(join(evidenceRoot, "domain", "table.md"), "# Curated table\n"); await git(["add", "workspace-content"], { cwd: join(ctx.run.root, "author") }); await git(["commit", "-m", "Bootstrap curated P1 content"], { cwd: join(ctx.run.root, "author") }); await git(["push", "origin", "main"], { cwd: join(ctx.run.root, "author") }); ctx.bootstrapCommit = (await git(["rev-parse", "HEAD"], { cwd: join(ctx.run.root, "author") })).stdout.trim(); } async function loadProductionBackend() { const [{ loadConfig }, { buildApp }, { WorkspaceRegistry }, { ThtRunner }] = await Promise.all([ import("../dist/config.js"), import("../dist/app.js"), import("../dist/workspaces/registry.js"), import("../dist/tht/tht-runner.js"), ]); return { loadConfig, buildApp, WorkspaceRegistry, ThtRunner }; } async function startProductionBackend(ctx, { name, env, runtimeConfigPath }) { const { loadConfig, buildApp, WorkspaceRegistry, ThtRunner } = await loadProductionBackend(); const config = loadConfig(env); const registry = new WorkspaceRegistry(config.workspaceRegistry); const thtRunner = new ThtRunner({ thtBin: config.thtBin, harnessDir: config.harnessDir, configPath: runtimeConfigPath, dataRoot: config.dataRoot, runtimeSnapshotRoot: join(config.workspaceRegistry.root, "snapshots", "runtime"), secretRoots: config.workspaceRegistry.secretRoots, secretsFile: config.secretsFile, secretFiles: config.secretFiles, semanticRuntime: { internalQdrantUrl: config.internalQdrantUrl, internalEmbeddingUrl: config.internalEmbeddingUrl, internalEmbeddingModel: config.internalEmbeddingModel, internalEmbeddingDimensions: config.internalEmbeddingDimensions }, }); const app = buildApp(config, { thtRunner, workspaceRegistry: registry }); let address; try { address = await app.listen({ host: "127.0.0.1", port: 0 }); } catch (error) { try { await app.close(); } catch { throw new Error("production listener start and close both failed"); } throw error; } const url = new URL(address); const baseUrl = `http://127.0.0.1:${url.port}`; ctx.networkGuard.addOwnedOrigin(baseUrl); const service = { name, app, baseUrl, registry, thtRunner, config }; ctx.services.push(service); await writeOwnership(ctx.run, { name, kind: "fastify", host: "127.0.0.1", requestedPort: 0, actualPort: Number(url.port), pid: process.pid, state: "listening", }); return service; } async function startBackend(ctx) { const service = await startProductionBackend(ctx, { name: "primary", env: ctx.env, runtimeConfigPath: join(ctx.run.root, "installation", "runtime", "base.yaml"), }); ctx.registryConfig = service.config.workspaceRegistry; ctx.registry = service.registry; ctx.thtRunner = service.thtRunner; ctx.app = service.app; ctx.baseUrl = service.baseUrl; } export function exportArchiveEvidencePath(requestId) { if (!/^export-[a-z0-9-]+$/.test(requestId)) throw new Error("invalid export request id"); return `exports/raw/${requestId}.zip`; } function trackArtifact(ctx, artifact) { if (ctx.activeArtifacts) { if (ctx.activeArtifacts.some(({ path }) => path === artifact.path)) throw new Error("artifact path is duplicated within check"); ctx.activeArtifacts.push(artifact); } return artifact; } async function request(ctx, id, method, path, body, binary = false, requestEvidence, baseUrl = ctx.baseUrl) { const requestSummary = requestEvidence === undefined ? { method, path, ...(body === undefined ? {} : { body: sanitizeForEvidence(body, ctx.forbiddenValues) }) } : { method, path, input: requestEvidence }; trackArtifact(ctx, await evidence(ctx.run, `requests/${id}.json`, requestSummary, ctx.forbiddenValues)); ctx.httpRequests.push({ method, path }); const response = await globalThis.fetch(`${baseUrl}${path}`, { method, headers: body === undefined ? {} : { "content-type": "application/json" }, ...(body === undefined ? {} : { body: JSON.stringify(body) }), signal: AbortSignal.timeout(15_000), }); if (binary) { const bytes = Buffer.from(await response.arrayBuffer()); await atomicWrite(join(ctx.run.root, exportArchiveEvidencePath(id)), bytes); trackArtifact(ctx, await evidence(ctx.run, `responses/${id}.json`, { status: response.status, contentType: response.headers.get("content-type"), bytes: bytes.length })); return { status: response.status, bytes }; } const text = await response.text(); let parsed; try { parsed = text ? JSON.parse(text) : null; } catch { parsed = { invalidJson: true }; } const safe = sanitizeForEvidence(parsed, ctx.forbiddenValues); trackArtifact(ctx, await evidence(ctx.run, `responses/${id}.json`, { status: response.status, body: safe }, ctx.forbiddenValues)); return { status: response.status, body: parsed }; } async function extractZip(ctx, id, bytes) { const yauzl = (await import("yauzl")).default; const output = join(ctx.run.root, "exports", "extracted", id); await mkdir(output, { recursive: true }); const files = await new Promise((resolvePromise, reject) => { yauzl.fromBuffer(bytes, { lazyEntries: true, strictFileNames: true, validateEntrySizes: true }, (error, zip) => { if (error || !zip) return reject(error ?? new Error("zip open failed")); const collected = new Map(); let total = 0; zip.on("error", reject); zip.on("end", () => resolvePromise(collected)); zip.on("entry", (entry) => { const type = (entry.externalFileAttributes >>> 16) & 0o170000; if (!ZIP_FILES.includes(entry.fileName) || entry.fileName.includes("\\") || entry.fileName.includes("..") || entry.fileName.startsWith("/") || entry.fileName.endsWith("/") || type === 0o120000 || collected.has(entry.fileName) || entry.uncompressedSize > 2_000_000) return reject(new Error("unsafe export entry")); zip.openReadStream(entry, (streamError, stream) => { if (streamError || !stream) return reject(streamError ?? new Error("zip stream failed")); const chunks = []; stream.on("data", (chunk) => { total += chunk.length; if (total > 8_000_000) reject(new Error("export too large")); else chunks.push(chunk); }); stream.on("end", () => { collected.set(entry.fileName, Buffer.concat(chunks)); zip.readEntry(); }); stream.on("error", reject); }); }); zip.readEntry(); }); }); assert(files.size === ZIP_FILES.length, "export file allowlist mismatch"); const manifest = JSON.parse(files.get("manifest.json").toString("utf8")); assert(manifest.schema_version === 1 && manifest.workspace_id === id, "export manifest identity mismatch"); for (const name of ZIP_FILES.slice(1)) assert(sha256(files.get(name)) === manifest.files[name], `export hash mismatch ${name}`); for (const [name, contents] of files) await atomicWrite(join(output, name), contents, 0o600); return manifest; } function assert(condition, message) { if (!condition) throw new Error(message); } function assertGenericWorkspaceInvalid(response, label) { assert(response.status === 400 && response.body?.code === "workspace_invalid", `${label} was not rejected through HTTP`); assert(Object.keys(response.body).sort().join(",") === "code,message", `${label} response envelope was not exact`); assert(response.body.message === "Workspace request or bundle is invalid.", `${label} response message was not generic`); assert(!/fatal:|stderr|git command|rev-parse|ls-tree/i.test(JSON.stringify(response.body)), `${label} exposed Git stderr`); } async function snapshotDigest(path) { const files = await walkFiles(path); const result = {}; for (const file of files) result[file.rel] = sha256(await readFile(file.path)); return result; } const SAFE_AMBIENT_ENV = Object.freeze(["LANG", "LC_ALL", "TZ"]); export function buildSafeEnvironment({ ambient = process.env, fixture = {} } = {}) { const safe = {}; for (const key of SAFE_AMBIENT_ENV) if (typeof ambient[key] === "string") safe[key] = ambient[key]; for (const [key, value] of Object.entries(fixture)) { if (typeof value !== "string") throw new Error(`fixture environment value must be a string: ${key}`); safe[key] = value; } return safe; } async function manifestFiles(root, paths) { const files = []; const visit = async (absolute, rel) => { const entry = await lstat(absolute); if (entry.isSymbolicLink()) throw new Error(`provenance path is a symlink: ${rel}`); if (entry.isDirectory()) { for (const child of (await readdir(absolute, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) { await visit(join(absolute, child.name), rel ? `${rel}/${child.name}` : child.name); } } else if (entry.isFile()) { const bytes = await readFile(absolute); files.push({ path: rel, bytes: bytes.length, sha256: sha256(bytes) }); } else throw new Error(`provenance path is not a regular file: ${rel}`); }; for (const path of paths) await visit(join(root, path), path); files.sort((a, b) => a.path.localeCompare(b.path)); return { files, manifestSha256: sha256(JSON.stringify(files)) }; } export async function collectRepositoryProvenance({ repositoryRoot, gitPath = resolveTrustedSystemExecutableSync("git") }) { const repo = canonicalRoot(repositoryRoot); const gitEnv = baseSafeGitEnvironment(gitPath); const readIdentity = async () => { const [head, tree, status] = await Promise.all([ runCommand({ executable: gitPath, argv: ["-C", repo, "rev-parse", "HEAD"], env: gitEnv }), runCommand({ executable: gitPath, argv: ["-C", repo, "rev-parse", "HEAD^{tree}"], env: gitEnv }), runCommand({ executable: gitPath, argv: ["-C", repo, "status", "--porcelain=v1", "--untracked-files=all"], env: gitEnv }), ]); return { head: head.stdout.trim(), tree: tree.stdout.trim(), status: status.stdout }; }; const before = await readIdentity(); if (!HEX40.test(before.head) || !HEX40.test(before.tree) || before.status !== "") throw new Error("repository is not clean at exact HEAD"); const backendRoot = join(repo, "backend"); const backendSource = await manifestFiles(backendRoot, [ "src", "scripts/p1-acceptance.mjs", "package.json", "package-lock.json", "tsconfig.json", ]); const backendDist = await manifestFiles(backendRoot, ["dist"]); const after = await readIdentity(); if (JSON.stringify(after) !== JSON.stringify(before)) throw new Error("repository provenance changed during binding"); return { schemaVersion: 1, head: before.head, tree: before.tree, clean: true, backendSource, backendDist }; } async function setupContext(run, repositoryRoot, env, ctx = {}) { const provenance = await collectRepositoryProvenance({ repositoryRoot, gitPath: resolveTrustedSystemExecutableSync("git") }); 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"); const ownedTmp = join(run.root, "installation", "runtime", "tmp"); await mkdir(ownedHome, { recursive: true, mode: 0o700 }); await mkdir(ownedTmp, { recursive: true, mode: 0o700 }); const executablePath = [...new Set([dirname(executables.gitPath), dirname(executables.pythonPath), dirname(executables.thtPath)])].join(":"); const fixtureEnv = { PATH: executablePath, HOME: ownedHome, TMPDIR: ownedTmp, GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_SYSTEM: "/dev/null", GIT_CONFIG_GLOBAL: "/dev/null", GIT_ATTR_NOSYSTEM: "1", GIT_TERMINAL_PROMPT: "0", GIT_ASKPASS: "/bin/false", SSH_ASKPASS: "/bin/false", GIT_ALLOW_PROTOCOL: "file", GIT_PROTOCOL_FROM_USER: "0", 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"), MAINTENANCE_STATE_FILE: join(run.root, "installation", "data", "maintenance.json"), THT_WORKSPACE_REGISTRY_ROOT: join(run.root, "installation", "registry"), THT_WORKSPACE_GIT_REMOTE: join(run.root, "remote.git"), THT_WORKSPACE_GIT_BRANCH: "main", THT_WORKSPACE_GIT_AUTHOR_NAME: "P1 API Publisher", THT_WORKSPACE_GIT_AUTHOR_EMAIL: "p1-api@example.invalid", THT_WORKSPACE_INSTALLATION_ID: "p1-acceptance", THT_WORKSPACE_SECRET_ROOTS: join(run.root, "fixture-secrets"), THT_HOME: join(run.root, "installation", "runtime", "tht-home"), PYTHONDONTWRITEBYTECODE: "1", PYTHONNOUSERSITE: "1", GIT_TRACE2_EVENT: gitTracePath, }; Object.assign(ctx, { run, repositoryRoot, descriptors: descriptors(), forbiddenValues: ctx.forbiddenValues ?? [], env: buildSafeEnvironment({ ambient: env, fixture: fixtureEnv }), httpRequests: [], services: [], gitTracePath, executables, provenance, expectedGitRepositories: ["remote.git", "author"], }); await createTopology(run); await setupSecrets(ctx); return ctx; } async function treeHash(path, excludedPrefixes = []) { const digest = await snapshotDigest(path); for (const key of Object.keys(digest)) if (excludedPrefixes.some((prefix) => key === prefix || key.startsWith(`${prefix}/`))) delete digest[key]; return sha256(JSON.stringify(digest)); } async function checkoutSemanticState(path) { const head = await git(["rev-parse", "HEAD"], { cwd: path }); const branch = await git(["symbolic-ref", "--short", "HEAD"], { cwd: path }); const statusResult = await git(["status", "--porcelain=v1"], { cwd: path }); const indexTree = await git(["write-tree"], { cwd: path }); const refs = await git(["show-ref"], { cwd: path }); return { head: head.stdout.trim(), branch: branch.stdout.trim(), status: statusResult.stdout, indexTree: indexTree.stdout.trim(), refs: sha256(refs.stdout), worktree: await treeHash(path, [".git"]), }; } async function bareSemanticState(path, branch) { const [head, tree, refs] = await Promise.all([ git(["--git-dir", path, "rev-parse", `refs/heads/${branch}`]), git(["--git-dir", path, "rev-parse", `refs/heads/${branch}^{tree}`]), git(["--git-dir", path, "show-ref"]), ]); return { head: head.stdout.trim(), tree: tree.stdout.trim(), refs: sha256(refs.stdout) }; } async function primarySemanticState(ctx) { return { remote: await bareSemanticState(join(ctx.run.root, "remote.git"), "main"), author: await checkoutSemanticState(join(ctx.run.root, "author")), checkout: await checkoutSemanticState(join(ctx.run.root, "installation", "registry", "repo")), active: await treeHash(join(ctx.run.root, "installation", "registry", "state")), snapshots: await treeHash(join(ctx.run.root, "installation", "registry", "snapshots")), data: await treeHash(join(ctx.run.root, "installation", "data")), runtime: await treeHash(join(ctx.run.root, "installation", "runtime"), ["contextual"]), }; } async function registryState(ctx) { return await primarySemanticState(ctx); } async function contextualSemanticState(root) { return { remote: await bareSemanticState(join(root, "remote.git"), "invalid-context"), author: await checkoutSemanticState(join(root, "author")), checkout: await checkoutSemanticState(join(root, "registry", "repo")), active: await treeHash(join(root, "registry", "state")), snapshots: await treeHash(join(root, "registry", "snapshots")), data: await treeHash(join(root, "data")), runtime: await treeHash(join(root, "runtime")), }; } async function invariantArtifact(ctx, path, value) { return trackArtifact(ctx, await evidence(ctx.run, path, value, ctx.forbiddenValues)); } function assertByteIdentical(left, right, label) { assert(JSON.stringify(left) === JSON.stringify(right), `${label} state changed`); } async function currentSnapshotManifest(ctx, commit) { const path = join(ctx.run.root, "installation", "registry", "snapshots", commit, "snapshot.json"); const manifest = JSON.parse(await readFile(path, "utf8")); assert(manifest.head === commit, "snapshot manifest head mismatch"); return { path, manifest }; } function revisionFromManifest(manifest, id) { const revision = manifest.revisions.find((candidate) => candidate.id === id); assert(revision, `manifest revision absent ${id}`); return revision; } function renderedRoot(parsed) { return parsed.evidence.sources[0].root; } function assertRuntimeContract(ctx, id, parsed, revision) { assert(parsed.runtime_identity.workspace_id === id, "runtime workspace identity mismatch"); assert(parsed.runtime_identity.workspace_revision === revision.commit, "runtime revision mismatch"); assert(parsed.runtime_identity.source_identity === `workspace://${id}`, "runtime source identity mismatch"); assert(parsed.vector.max_chunk_chars === 4000 && parsed.vector.retain_published_generations === 3, "runtime policy mismatch"); const source = parsed.evidence.sources[0]; if (id === "p1-filesystem") { const exactRoot = join(dirname(revision.snapshotPath), "workspace-content", id, "evidence"); assert(source.type === "filesystem" && source.root === exactRoot, "filesystem root mismatch"); assert(JSON.stringify(source.patterns) === JSON.stringify(["**/*.md"]) && source.max_bytes === 10485760, "filesystem source contract mismatch"); assert(!existsSync(source.root), "filesystem Evidence root was materialized"); } else if (id === "p1-http") { assert(source.type === "http", "HTTP source type mismatch"); assert(JSON.stringify(source.provenance_urls) === JSON.stringify(["https://evidence.example.test/guide.md"]), "HTTP provenance mismatch"); assert(source.signed_urls_file === join(ctx.run.root, "fixture-secrets", "evidence-signed-urls.json"), "HTTP binding mismatch"); assert(source.connect_timeout === 1.25 && source.read_timeout === 30.001 && source.max_bytes === 12345 && source.max_redirects === 2 && source.allow_private_hosts === false && source.max_cache_bytes === 67890, "HTTP limits mismatch"); } else if (id === "p1-s3") { assert(source.type === "s3" && source.bucket === "p1-evidence" && source.prefix === "published/", "S3 identity mismatch"); assert(source.endpoint_url === "https://s3.example.test/" && source.region === "eu-west-1", "S3 endpoint mismatch"); assert(source.access_key_file === join(ctx.run.root, "fixture-secrets", "evidence-access") && source.secret_key_file === join(ctx.run.root, "fixture-secrets", "evidence-secret") && source.session_token_file === join(ctx.run.root, "fixture-secrets", "evidence-session"), "S3 bindings mismatch"); assert(source.trusted_endpoint === true && source.allow_private_endpoint === false && source.allow_insecure_endpoint === false && source.max_bytes === 12345 && source.max_objects === 33 && source.max_pages === 4 && source.page_size === 5, "S3 limits mismatch"); } } async function traceSize(path) { try { return (await stat(path)).size; } catch (error) { if (error.code === "ENOENT") return 0; throw error; } } function uniqueArtifacts(artifacts) { const seen = new Set(); for (const artifact of artifacts) { if (seen.has(artifact.path)) throw new Error("artifact path is duplicated within check"); seen.add(artifact.path); } return artifacts; } async function commandsObservedForCheck(ctx, checkId, traceBefore) { const commands = new Set((commandEventSink ?? []).filter((event) => event.checkId === checkId).map((event) => event.executable)); if (await traceSize(ctx.gitTracePath) > traceBefore) commands.add("git"); return [...commands].sort(); } function wrapProductionCheck(ctx, scenario) { return { id: scenario.id, run: async () => { ctx.activeArtifacts = []; activeCommandCheckId = scenario.id; const traceBefore = await traceSize(ctx.gitTracePath); try { const output = await scenario.run(); return { commands: await commandsObservedForCheck(ctx, scenario.id, traceBefore), artifacts: uniqueArtifacts([...(output.artifacts ?? []), ...ctx.activeArtifacts]), }; } catch (error) { error.acceptancePartial = { commands: await commandsObservedForCheck(ctx, scenario.id, traceBefore), artifacts: uniqueArtifacts(ctx.activeArtifacts), }; throw error; } finally { activeCommandCheckId = undefined; ctx.activeArtifacts = undefined; } }, }; } async function assertProductionGitTrace(ctx) { const text = await readFile(ctx.gitTracePath, "utf8"); const events = text.split("\n").filter(Boolean).map((line) => JSON.parse(line)); const productionStarts = events.filter((event) => event.event === "start" && Array.isArray(event.argv) && event.argv.some((arg) => typeof arg === "string" && arg.startsWith("core.hooksPath="))); const publication = productionStarts.some(({ argv }) => argv.includes("commit") && argv.some((arg) => /^Publish workspace /.test(arg))); const pull = productionStarts.some(({ argv }) => argv.includes("fetch")); assert(publication && pull, "production Git publication/pull operations absent from Trace2 evidence"); return { eventCount: events.length, productionStartCount: productionStarts.length, publication, pull }; } async function assertNoP1ScopeEntrypoints(ctx) { const workspacesRoot = join(ctx.repositoryRoot, "backend", "dist", "workspaces"); const modules = (await readdir(workspacesRoot)).filter((name) => name.endsWith(".js")); const forbiddenModuleNames = modules.filter((name) => /evidence[-_.]?(?:adapter|acquisition|preprocess)|(?:acquisition|preprocess)[-_.]?evidence/i.test(name)); const forbiddenExports = []; for (const name of modules) { const source = await readFile(join(workspacesRoot, name), "utf8"); if (/export\s+(?:class|function|const)\s+(?:acquire|preprocess)Evidence|export\s+(?:class|function|const)\s+Evidence(?:Adapter|Acquisition|Preprocessor)/.test(source)) forbiddenExports.push(name); } const routeSurfaces = ctx.services.map(({ name, app }) => ({ name, routes: app.printRoutes({ commonPrefix: false }) })); const forbiddenRoutes = routeSurfaces.filter(({ routes }) => /\/(?:evidence|acquisition|preprocess)(?:\W|$)/i.test(routes)); const packageJson = JSON.parse(await readFile(join(ctx.repositoryRoot, "backend", "package.json"), "utf8")); const entrypointBytes = JSON.stringify({ main: packageJson.main, bin: packageJson.bin, exports: packageJson.exports, scripts: packageJson.scripts }); const forbiddenPackageEntrypoints = /(?:acquire|preprocess)Evidence|Evidence(?:Adapter|Acquisition|Preprocessor)/i.test(entrypointBytes); const auditedSurfaceFiles = []; for (const group of ["workspaces", "routes"]) { const root = join(ctx.repositoryRoot, "backend", "dist", group); for (const name of (await readdir(root)).filter((value) => value.endsWith(".js")).sort()) { auditedSurfaceFiles.push({ path: `${group}/${name}`, source: await readFile(join(root, name), "utf8") }); } } const prohibitedProcessSurfaces = auditedSurfaceFiles.filter(({ source }) => /node:(?:dgram|worker_threads|dns)|\.node(?:["']|$)|process\.dlopen|node-gyp|bindings\s*\(/.test(source)); const networkAdapterModules = auditedSurfaceFiles.filter(({ source }) => /node:(?:net|http|https)|globalThis\.fetch|\bfetch\s*\(/.test(source)).map(({ path }) => path); const childProcessModules = auditedSurfaceFiles.filter(({ source }) => /node:child_process/.test(source)).map(({ path }) => path); assert(JSON.stringify(networkAdapterModules) === JSON.stringify(["workspaces/diagnostics.js"]) && JSON.stringify(childProcessModules) === JSON.stringify(["workspaces/diagnostics.js", "workspaces/git-repository.js"]) && prohibitedProcessSurfaces.length === 0, "unexpected production process/network adapter entrypoint surface present"); assert(forbiddenModuleNames.length === 0 && forbiddenExports.length === 0 && forbiddenRoutes.length === 0 && !forbiddenPackageEntrypoints, "prohibited P1 adapter/acquisition/preprocessing entrypoint surface present"); return { moduleFilesAudited: modules.sort(), routeAppsAudited: routeSurfaces.map(({ name }) => name), packageEntrypointsAudited: true, productionSurfaceFilesAudited: auditedSurfaceFiles.map(({ path }) => path), networkAdapterModules, childProcessModules, workerDgramDnsNativeEntrypoints: [], }; } function productionChecks(ctx) { const log = async (id, value) => ({ commands: [], artifacts: [await evidence(ctx.run, `logs/${id}.json`, value, ctx.forbiddenValues)] }); const scenarios = [ { id: "preflight", run: async () => { const gitVersion = await git(["--version"]); await access(ctx.env.THT_BIN, fsConstants.X_OK); const preflight = await log("preflight", { git: gitVersion.stdout.trim(), node: process.version, thtExecutable: true, repositoryHead: ctx.provenance.head, repositoryTree: ctx.provenance.tree, repositoryClean: ctx.provenance.clean }); preflight.artifacts.push(await evidence(ctx.run, "logs/provenance.json", ctx.provenance)); return preflight; } }, { id: "clean_state", run: async () => { assert(RUN_ID.test(ctx.run.runId), "run identity invalid"); return await log("clean_state", { exclusiveRoot: true, reused: false }); } }, { id: "ownership", run: async () => { await readAndValidateOwnership({ repositoryRoot: ctx.repositoryRoot, runRoot: ctx.run.root, expectedNonce: ctx.run.nonce }); return await log("ownership", { valid: true, listener: "not_started" }); } }, { id: "local_git_bootstrap", run: async () => { await initializeGit(ctx); const descriptorArtifacts = []; for (const workspace of ctx.descriptors) { const path = `fixtures/descriptors/${workspace.workspace.id}.json`; await atomicWrite(join(ctx.run.root, path), `${JSON.stringify(workspace, null, 2)}\n`); descriptorArtifacts.push(await fileArtifact(ctx.run.root, path)); } assert(!existsSync(join(ctx.run.root, "author", "workspaces")), "fixture authored a descriptor"); return { artifacts: [await evidence(ctx.run, "logs/local_git_bootstrap.json", { bootstrapCommit: ctx.bootstrapCommit, descriptorEmpty: true }), ...descriptorArtifacts] }; } }, { id: "http_validate_publish_pull_read_export", run: async () => { await startBackend(ctx); const status = await request(ctx, "registry-status", "GET", "/workspace-registry/status"); assert(status.status === 200 && status.body.head === ctx.bootstrapCommit, "empty registry status failed"); let base = status.body.head; for (const workspace of ctx.descriptors) { const id = workspace.workspace.id; const validated = await request(ctx, `validate-${id}`, "POST", "/workspaces/validate", { workspace }); assert(validated.status === 200 && validated.body.workspace.workspace.id === id, `validation failed ${id}`); const published = await request(ctx, `publish-${id}`, "POST", "/workspaces/publish", { action: "create", workspace, baseCommit: base }); assert(published.status === 200 && HEX40.test(published.body.revision.commit), `publication failed ${id}`); base = published.body.revision.commit; } ctx.publicationHead = base; const pulled = await request(ctx, "registry-pull", "POST", "/workspace-registry/pull"); assert(pulled.status === 200 && pulled.body.head === base, "pull failed"); const listed = await request(ctx, "workspace-list", "GET", "/workspaces"); assert(listed.status === 200 && listed.body.length === 3, "list failed"); ctx.reads = {}; ctx.exportManifests = {}; const artifacts = []; for (const workspace of ctx.descriptors) { const id = workspace.workspace.id; const read = await request(ctx, `read-${id}`, "GET", `/workspaces/${id}`); assert(read.status === 200, `read failed ${id}`); ctx.reads[id] = read.body; const exported = await request(ctx, `export-${id}`, "GET", `/workspaces/${id}/export`, undefined, true); assert(exported.status === 200, `export failed ${id}`); ctx.exportManifests[id] = await extractZip(ctx, id, exported.bytes); artifacts.push(await fileArtifact(ctx.run.root, exportArchiveEvidencePath(`export-${id}`))); for (const name of ZIP_FILES) artifacts.push(await fileArtifact(ctx.run.root, `exports/extracted/${id}/${name}`)); } artifacts.unshift(await evidence(ctx.run, "logs/http-flow.json", { workspaceIds: Object.keys(ctx.reads), head: base, realListener: true, fetch: true })); return { commands: [], artifacts }; } }, { id: "same_revision_git_objects", run: async () => { const revision = ctx.reads["p1-filesystem"].revision; ctx.oldRevision = revision; ctx.oldSnapshotDigest = await snapshotDigest(dirname(revision.snapshotPath)); const checkoutHead = (await git(["rev-parse", "HEAD"], { cwd: join(ctx.run.root, "installation", "registry", "repo") })).stdout.trim(); const manifestPath = join(dirname(revision.snapshotPath), "snapshot.json"); const manifest = JSON.parse(await readFile(manifestPath, "utf8")); const lease = ctx.thtRunner.acquireWorkspaceRuntime(revision.snapshotPath); let rendered; try { rendered = (await import("yaml")).parse(await readFile(lease.path, "utf8")); } finally { lease.release(); } ctx.oldFilesystemRoot = renderedRoot(rendered); const identities = [revision.commit, checkoutHead, manifest.head, rendered.runtime_identity.workspace_revision]; assert(new Set(identities).size === 1, "revision identities diverged"); const repo = join(ctx.run.root, "installation", "registry", "repo"); await git(["cat-file", "-e", `${revision.commit}:workspaces/p1-filesystem.yaml`], { cwd: repo }); await git(["cat-file", "-e", `${revision.commit}:workspace-content/p1-filesystem/evidence/guide.md`], { cwd: repo }); const type = (await git(["cat-file", "-t", `${revision.commit}:workspace-content/p1-filesystem/evidence`], { cwd: repo })).stdout.trim(); assert(type === "tree", "Evidence object is not a tree"); assert(!existsSync(join(dirname(revision.snapshotPath), "workspace-content")), "snapshot materialized workspace-content"); return { commands: ["git"], artifacts: [ await evidence(ctx.run, "logs/git-object-proof.json", { commit: revision.commit, checkoutHead, manifestHead: manifest.head, runtimeRevision: rendered.runtime_identity.workspace_revision, filesystemRoot: ctx.oldFilesystemRoot, evidenceType: type }), await fileArtifact(ctx.run.root, relative(ctx.run.root, manifestPath)), await fileArtifact(ctx.run.root, relative(ctx.run.root, revision.snapshotPath)), ] }; } }, { id: "content_only_revision", run: async () => { const author = join(ctx.run.root, "author"); await git(["fetch", "origin", "main"], { cwd: author }); await git(["reset", "--hard", "origin/main"], { cwd: author }); const descriptorBefore = (await git(["rev-parse", "HEAD:workspaces/p1-filesystem.yaml"], { cwd: author })).stdout.trim(); await writeFile(join(author, "workspace-content", "p1-filesystem", "evidence", "guide.md"), "# P1 curated Evidence v2\n"); await git(["add", "workspace-content/p1-filesystem/evidence/guide.md"], { cwd: author }); await git(["commit", "-m", "Update curated Evidence only"], { cwd: author }); await git(["push", "origin", "main"], { cwd: author }); ctx.contentCommit = (await git(["rev-parse", "HEAD"], { cwd: author })).stdout.trim(); const pulled = await request(ctx, "content-only-pull", "POST", "/workspace-registry/pull"); assert(pulled.status === 200 && pulled.body.head === ctx.contentCommit, "content pull failed"); const currentRead = (await request(ctx, "read-filesystem-content", "GET", "/workspaces/p1-filesystem")).body; const current = currentRead.revision; const descriptorAfter = (await git(["rev-parse", "HEAD:workspaces/p1-filesystem.yaml"], { cwd: author })).stdout.trim(); assert(current.commit === ctx.contentCommit && current.blob === ctx.oldRevision.blob && descriptorAfter === descriptorBefore, "content revision identity failed"); assertByteIdentical(await snapshotDigest(dirname(ctx.oldRevision.snapshotPath)), ctx.oldSnapshotDigest, "old snapshot"); const lease = ctx.thtRunner.acquireWorkspaceRuntime(current.snapshotPath); let rendered; try { rendered = (await import("yaml")).parse(await readFile(lease.path, "utf8")); } finally { lease.release(); } const newRoot = renderedRoot(rendered); const expectedOldRoot = join(dirname(ctx.oldRevision.snapshotPath), "workspace-content", "p1-filesystem", "evidence"); const expectedNewRoot = join(dirname(current.snapshotPath), "workspace-content", "p1-filesystem", "evidence"); assert(ctx.oldFilesystemRoot === expectedOldRoot, "old filesystem root was not old commit-addressed root"); assert(newRoot === expectedNewRoot && newRoot !== ctx.oldFilesystemRoot, "new filesystem root did not change exactly with commit"); ctx.currentRevision = current; const currentSnapshot = await currentSnapshotManifest(ctx, current.commit); ctx.currentManifest = currentSnapshot.manifest; return { commands: ["git"], artifacts: [ await evidence(ctx.run, "logs/content-only-revision.json", { oldCommit: ctx.oldRevision.commit, newCommit: current.commit, descriptorBlob: current.blob, oldFilesystemRoot: ctx.oldFilesystemRoot, newFilesystemRoot: newRoot, oldSnapshotImmutable: true }), ] }; } }, { id: "snapshot_and_docs", run: async () => { const artifacts = []; for (const id of ctx.descriptors.map((item) => item.workspace.id)) { const extracted = join(ctx.run.root, "exports", "extracted", id); for (const name of ZIP_FILES) { assert((await lstat(join(extracted, name))).isFile(), `missing extracted ${name}`); await access(join(extracted, name), fsConstants.R_OK); } const revision = revisionFromManifest(ctx.currentManifest, id); for (const suffix of [".yaml", ".env.example", ".md"]) { const file = join(dirname(revision.snapshotPath), `${id}${suffix}`); assert(existsSync(file), `snapshot artifact absent ${file}`); artifacts.push(await fileArtifact(ctx.run.root, relative(ctx.run.root, file))); } assert(!existsSync(join(dirname(revision.snapshotPath), "workspace-content")), "snapshot materialized source tree"); } artifacts.push(await fileArtifact(ctx.run.root, relative(ctx.run.root, join(dirname(ctx.currentRevision.snapshotPath), "snapshot.json")))); artifacts.unshift(await evidence(ctx.run, "logs/snapshot-and-docs.json", { exactBundleFiles: ZIP_FILES, generatedDocs: true, immutableSnapshots: true, derivedFromManifest: true })); return { commands: [], artifacts }; } }, { id: "runtime_render_determinism", run: async () => { ctx.configChecks = []; const artifacts = []; const YAML = await import("yaml"); for (const id of ctx.descriptors.map((item) => item.workspace.id)) { const revision = revisionFromManifest(ctx.currentManifest, id); const bytes = []; for (let n = 1; n <= 2; n += 1) { const lease = ctx.thtRunner.acquireWorkspaceRuntime(revision.snapshotPath); try { const contents = await readFile(lease.path); bytes.push(contents); await atomicWrite(join(ctx.run.root, "rendered", `${id}-${n}.yaml`), contents); const checked = await tht(ctx.env.THT_BIN, ["config", "check", "-c", lease.path], { cwd: ctx.env.THT_HARNESS_DIR, env: ctx.env, timeoutMs: 30_000 }); ctx.configChecks.push({ id, observation: n, code: checked.code }); } finally { lease.release(); } const runtimeDir = join(ctx.run.root, "installation", "registry", "snapshots", "runtime"); if (existsSync(runtimeDir)) assert((await readdir(runtimeDir)).length === 0, "runtime lease leaked"); artifacts.push(await fileArtifact(ctx.run.root, `rendered/${id}-${n}.yaml`)); } assert(bytes[0].equals(bytes[1]), `render nondeterministic ${id}`); assertRuntimeContract(ctx, id, YAML.parse(bytes[0].toString("utf8")), revision); } artifacts.unshift(await evidence(ctx.run, "logs/runtime-render.json", { deterministic: true, released: true, fullSourcePolicyAssertions: true, rootsUnmaterialized: true, workspaces: ctx.descriptors.map((item) => item.workspace.id) })); return { commands: ["tht"], artifacts }; } }, { id: "tht_config_check", run: async () => { assert(ctx.configChecks.length === 6 && ctx.configChecks.every(({ code }) => code === 0), "tht config checks incomplete"); return { commands: ["tht"], artifacts: [await evidence(ctx.run, "logs/tht-config-check.json", ctx.configChecks)] }; } }, { id: "negative_schema_cases", run: async () => { const base = structuredClone(ctx.descriptors[0]); const cases = [ ["absolute", (w) => { w.evidence.source.uri = "/tmp/evidence"; }, "evidence.source.uri"], ["traversal", (w) => { w.evidence.source.uri = "workspace-content/p1-filesystem/../evidence"; }, "evidence.source.uri"], ["backslash", (w) => { w.evidence.source.uri = "workspace-content\\p1-filesystem\\evidence"; }, "evidence.source.uri"], ["cross-workspace", (w) => { w.evidence.source.uri = "workspace-content/other/evidence"; }, "evidence.source.uri"], ["unsupported-source", (w) => { w.evidence.source.type = "ftp"; w.evidence.source.uri = "ftp://example.test/file"; }, "evidence.source.type"], ["unsupported-protocol", (w) => { w.evidence.source = { type: "http", uris: ["ftp://evidence.example.test/file"], authentication: "none" }; }, "evidence.source.uris"], ["credential-field", (w) => { w.evidence.source.password = ctx.secretValues.rejected; }, "evidence.source.password"], ["http-userinfo-query", (w) => { w.evidence.source = { type: "http", uris: [`https://user:${ctx.secretValues.rejected}@evidence.example.test/guide?x=${ctx.secretValues.rejected}`], authentication: "none" }; }, "evidence.source.uris"], ["malformed-policy", (w) => { w.evidence.policy.max_chunk_chars = 0; }, "evidence.policy.max_chunk_chars"], ["malformed-limit", (w) => { w.evidence.source.max_bytes = 0; }, "evidence.source.max_bytes"], ]; const outcomes = []; for (const [id, mutate, field] of cases) { const before = await registryState(ctx); const workspace = structuredClone(base); mutate(workspace); const safeInput = negativeRequestEvidence(id, field); trackArtifact(ctx, await evidence(ctx.run, `fixtures/requests/negative-${id}.json`, safeInput)); const response = await request(ctx, `negative-${id}`, "POST", "/workspaces/validate", { workspace }, false, safeInput); assertGenericWorkspaceInvalid(response, `negative ${id}`); assert(JSON.stringify(response.body).includes(ctx.secretValues.rejected) === false, `negative leaked ${id}`); assertByteIdentical(await registryState(ctx), before, `negative ${id}`); outcomes.push({ case: id, status: response.status, code: response.body.code, expectedInputField: field, genericSafeEnvelope: true, stateByteIdentical: true }); } return { commands: ["git"], artifacts: [await evidence(ctx.run, "logs/negative-schema.json", outcomes)] }; } }, { id: "negative_context_case", run: async () => { const contextual = join(ctx.run.root, "installation", "runtime", "contextual"); const contextualRemote = join(contextual, "remote.git"); const contextualAuthor = join(contextual, "author"); const primaryBefore = await primarySemanticState(ctx); assert(primaryBefore.remote.head === ctx.contentCommit, "primary main was not last-valid before contextual scenario"); await mkdir(contextual, { recursive: true }); await git(["clone", "--bare", join(ctx.run.root, "remote.git"), contextualRemote], { cwd: contextual }); await git(["clone", contextualRemote, contextualAuthor], { cwd: contextual }); await git(["config", "user.name", "P1 Context Curator"], { cwd: contextualAuthor }); await git(["config", "user.email", "p1-context@example.invalid"], { cwd: contextualAuthor }); await git(["checkout", "-b", "invalid-context"], { cwd: contextualAuthor }); await git(["push", "-u", "origin", "invalid-context"], { cwd: contextualAuthor }); await mkdir(join(contextual, "runtime"), { recursive: true }); await mkdir(join(contextual, "data"), { recursive: true }); await atomicWrite(join(contextual, "runtime", "base.yaml"), "{}\n"); const contextualEnv = buildSafeEnvironment({ ambient: ctx.env, fixture: { ...ctx.env, THT_DATA_ROOT: join(contextual, "data"), SETTINGS_FILE: join(contextual, "data", "settings.json"), MAINTENANCE_STATE_FILE: join(contextual, "data", "maintenance.json"), THT_WORKSPACE_REGISTRY_ROOT: join(contextual, "registry"), THT_WORKSPACE_GIT_REMOTE: contextualRemote, THT_WORKSPACE_GIT_BRANCH: "invalid-context", THT_WORKSPACE_INSTALLATION_ID: "p1-contextual-acceptance", THT_HOME: join(contextual, "runtime", "tht-home"), } }); const contextualService = await startProductionBackend(ctx, { name: "contextual", env: contextualEnv, runtimeConfigPath: join(contextual, "runtime", "base.yaml"), }); ctx.expectedGitRepositories.push( "installation/runtime/contextual/remote.git", "installation/runtime/contextual/author", ); const contextualStatus = await request(ctx, "context-registry-status", "GET", "/workspace-registry/status", undefined, false, undefined, contextualService.baseUrl); assert(contextualStatus.status === 200 && contextualStatus.body.head === ctx.contentCommit, "contextual registry bootstrap failed"); const contextualBaselinePull = await request(ctx, "context-registry-baseline-pull", "POST", "/workspace-registry/pull", undefined, false, undefined, contextualService.baseUrl); assert(contextualBaselinePull.status === 200 && contextualBaselinePull.body.head === ctx.contentCommit, "contextual baseline pull failed"); const primaryAfterSetup = await primarySemanticState(ctx); await invariantArtifact(ctx, "logs/negative-context-setup-state.json", { before: primaryBefore, after: primaryAfterSetup }); assertByteIdentical(primaryAfterSetup, primaryBefore, "primary state during contextual setup"); const missing = baseWorkspace("missing-context", { type: "filesystem", uri: "workspace-content/missing-context/evidence", patterns: ["**/*.md"], max_bytes: 100 }); const missingFixture = "fixtures/descriptors/missing-context.json"; await atomicWrite(join(ctx.run.root, missingFixture), `${JSON.stringify(missing, null, 2)}\n`); trackArtifact(ctx, await fileArtifact(ctx.run.root, missingFixture)); const missingBefore = { primary: await primarySemanticState(ctx), secondary: await contextualSemanticState(contextual) }; await invariantArtifact(ctx, "logs/negative-context-missing-before.json", missingBefore); const rejectedPublish = await request(ctx, "context-missing-publish", "POST", "/workspaces/publish", { action: "create", workspace: missing, baseCommit: ctx.contentCommit }, false, undefined, contextualService.baseUrl); const missingAfter = { primary: await primarySemanticState(ctx), secondary: await contextualSemanticState(contextual) }; await invariantArtifact(ctx, "logs/negative-context-missing-after.json", missingAfter); assertGenericWorkspaceInvalid(rejectedPublish, "context publish"); assertByteIdentical(missingAfter.secondary, missingBefore.secondary, "failed contextual publish full semantic state"); assertByteIdentical(missingAfter.primary, primaryBefore, "primary state after contextual publish"); await rm(join(contextualAuthor, "workspace-content", "p1-filesystem", "evidence"), { recursive: true }); await git(["add", "-A", "workspace-content/p1-filesystem/evidence"], { cwd: contextualAuthor }); await git(["commit", "-m", "Invalid contextual Evidence state"], { cwd: contextualAuthor }); await git(["push", "origin", "invalid-context"], { cwd: contextualAuthor }); const invalidRemoteCommit = (await git(["rev-parse", "HEAD"], { cwd: contextualAuthor })).stdout.trim(); const invalidBefore = { primary: await primarySemanticState(ctx), secondary: await contextualSemanticState(contextual) }; await invariantArtifact(ctx, "logs/negative-context-invalid-before.json", invalidBefore); const rejectedPull = await request(ctx, "context-invalid-pull", "POST", "/workspace-registry/pull", undefined, false, undefined, contextualService.baseUrl); const invalidAfter = { primary: await primarySemanticState(ctx), secondary: await contextualSemanticState(contextual) }; await invariantArtifact(ctx, "logs/negative-context-invalid-after.json", invalidAfter); assertGenericWorkspaceInvalid(rejectedPull, "context pull"); assertByteIdentical(invalidAfter.primary, primaryBefore, "primary state after contextual pull"); assertByteIdentical(invalidAfter.secondary.remote, invalidBefore.secondary.remote, "pull mutated contextual fixture remote"); assertByteIdentical(invalidAfter.secondary.author, invalidBefore.secondary.author, "pull mutated contextual fixture author"); for (const key of ["active", "snapshots", "data", "runtime"]) { assert(invalidAfter.secondary[key] === invalidBefore.secondary[key], `invalid pull changed last-valid ${key}`); } assert(invalidBefore.secondary.checkout.head === ctx.contentCommit, "contextual checkout was not last-valid before invalid pull"); assert(invalidAfter.secondary.checkout.head === invalidRemoteCommit, "invalid checkout did not advance as explicitly allowed"); assert(invalidAfter.secondary.checkout.refs !== invalidBefore.secondary.checkout.refs, "invalid checkout refs did not advance as explicitly allowed"); assert(invalidAfter.secondary.checkout.branch === "invalid-context" && invalidAfter.secondary.checkout.status === "", "invalid checkout branch/status mismatch"); assert(invalidAfter.secondary.checkout.indexTree === invalidAfter.secondary.remote.tree, "invalid checkout index did not match invalid remote tree"); assert(invalidAfter.primary.remote.head === ctx.contentCommit, "contextual scenario mutated primary main"); return { artifacts: [await evidence(ctx.run, "logs/negative-context.json", { realSecondaryHttp: true, secondaryBranch: "invalid-context", primaryFullSemanticStateByteIdentical: true, primaryMainUnchanged: true, missingPublishSecondaryFullSemanticStateByteIdentical: true, invalidRemoteCommit, checkoutHeadIndexRefsAdvancedExplicitlyAllowed: true, remoteUnchangedByRequest: true, lastValidActiveSnapshotsDataRuntimeByteIdentical: true, exactGenericEnvelopesNoStderr: true, gitTransportTelemetryExcluded: [".git/logs", ".git/FETCH_HEAD", ".git/ORIG_HEAD", ".git/objects"], }, ctx.forbiddenValues)] }; } }, { id: "no_p1_scope_artifacts", run: async () => { const forbidden = ["artifacts/evidence", "corpus/ACTIVE", "embedding-output", "qdrant-records", "preprocessing-invocation"]; const files = (await walkFiles(ctx.run.root)).map(({ rel }) => rel); const present = files.filter((path) => forbidden.some((part) => path.includes(part))); const prohibitedRoutesCalled = ctx.httpRequests.filter(({ path }) => /\/evidence|\/acquisition|\/preprocess/i.test(path)); const prohibitedCommands = (commandEventSink ?? []).filter(({ argvLabels }) => argvLabels.some((label) => /preprocess|acquire.*evidence|embedding|qdrant/i.test(label))); const surfaceAudit = await assertNoP1ScopeEntrypoints(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, ...(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)), "production child allowlist evidence is incomplete"); assert(ctx.services.length === 2 && ctx.services.every(({ baseUrl }) => ctx.networkGuard.hasOwnedOrigin(baseUrl)), "listener was not an owned loopback origin"); return await log("no-p1-scope-artifacts", { absentArtifacts: forbidden, moduleEntrypointSurfaceAbsent: true, routeEntrypointSurfaceAbsent: true, ...surfaceAudit, productionGitTrace: gitTraceProof, networkGuardInstalledBeforeProduction: true, externalNetworkAttempts: [], exactProductionChildApi: true, productionChildKinds: [...childKinds].sort(), rejectedProductionChildren: [], ownedLoopbackOrigins: ctx.services.map(({ name }) => name), }); } }, { id: "secret_scan", run: async () => { const scan = await scanSecretsDetailed({ runRoot: ctx.run.root, forbiddenValues: ctx.forbiddenValues, expectedGitRepositories: ctx.expectedGitRepositories, }); assert(scan.findings.length === 0, "secret canary found outside exclusion"); ctx.gitSecretScanFrozen = true; return await log("secret-scan", { scanned: true, gitEnumerationFailClosed: true, excluded: "fixture-secrets", expectedGitRepositories: [...ctx.expectedGitRepositories], repositories: scan.repositories, findings: [], }); } }, { id: "cleanup_confinement", run: async () => { const fakeRepo = join(ctx.run.root, "installation", "runtime", "cleanup-test"); await mkdir(join(fakeRepo, ".artifacts", "p1-integration"), { recursive: true }); const synthetic = await createOwnedRun({ repositoryRoot: fakeRepo }); const sibling = join(fakeRepo, ".artifacts", "p1-integration", `p1-${"e".repeat(32)}`); await mkdir(sibling); await writeFile(join(sibling, "sentinel"), "foreign"); await cleanupOwnedRun({ repositoryRoot: fakeRepo, runRoot: synthetic.root, expectedNonce: synthetic.nonce }); assert(await readFile(join(sibling, "sentinel"), "utf8") === "foreign", "cleanup removed sibling"); await rm(fakeRepo, { recursive: true }); assert(!existsSync(fakeRepo), "cleanup test resource remained"); return await log("cleanup-confinement", { ownedRemoved: true, siblingPreservedDuringAssertion: true, testResourceRemoved: true }); } }, ]; return scenarios.map((scenario) => wrapProductionCheck(ctx, scenario)); } async function assertRejectsCode(fn, code) { try { await fn(); } catch (error) { if (error?.code === code) return; throw error; } throw new Error(`expected ${code}`); } function replaceProcessEnvironment(values) { for (const key of Object.keys(process.env)) delete process.env[key]; Object.assign(process.env, values); } function failedCheck(id, startedAt, error) { return { id, status: "FAIL", startedAt, finishedAt: nowIso(), commands: [], artifacts: [], error }; } function completeFailedResults(results, firstError = "Acceptance setup failed safely.") { const completed = [...results]; for (let index = completed.length; index < CHECK_IDS.length; index += 1) { completed.push(failedCheck(CHECK_IDS[index], nowIso(), index === 0 ? firstError : "Not executed after earlier failure.")); } return completed; } function assertUniqueResultArtifacts(results) { const seen = new Set(); for (const result of results) for (const artifact of result.artifacts) { if (seen.has(artifact.path)) throw new Error("artifact path is duplicated across checks"); seen.add(artifact.path); } return results; } async function verifyDeclaredArtifacts(runRoot, results) { assertUniqueResultArtifacts(results); for (const result of results) for (const artifact of result.artifacts) { safeArtifactPath(artifact.path); const current = sha256(await readFile(join(runRoot, artifact.path))); if (current !== artifact.sha256) throw new Error("declared artifact hash mismatch"); } } function minimalFailClosedReport(run, keep) { const checks = CHECK_IDS.map((id, index) => failedCheck( id, nowIso(), index === 0 ? "Acceptance audit failed closed." : "Not executed after fail-closed audit.", )); return { schemaVersion: 1, runId: run.runId, startedAt: run.startedAt, finishedAt: nowIso(), command: `p1-acceptance integration${keep ? " --keep" : ""}`, overall: "FAIL", checks, }; } function reportBytes(report) { validateReport(report); return { json: Buffer.from(`${JSON.stringify(report, null, 2)} `), markdown: Buffer.from(renderReportMarkdown(report)), }; } function attachResultArtifact(results, checkId, artifact) { const result = results.find(({ id }) => id === checkId); if (!result) throw new Error("trace artifact owner is absent"); result.artifacts.push(artifact); } async function listenerRefuses(baseUrl, timeoutMs = 1_000) { const url = new URL(baseUrl); return await new Promise((resolvePromise) => { const socket = new Socket(); let settled = false; const finish = (refuses) => { if (settled) return; settled = true; socket.destroy(); resolvePromise(refuses); }; const timer = setTimeout(() => finish(false), timeoutMs); timer.unref(); socket.once("connect", () => { clearTimeout(timer); finish(false); }); socket.once("error", () => { clearTimeout(timer); finish(true); }); try { socket.connect({ host: "127.0.0.1", port: Number(url.port) }); } catch { clearTimeout(timer); finish(true); } }); } function environmentMatches(expected) { const currentKeys = Object.keys(process.env).sort(); const expectedKeys = Object.keys(expected).sort(); return JSON.stringify(currentKeys) === JSON.stringify(expectedKeys) && expectedKeys.every((key) => process.env[key] === expected[key]); } export async function runIntegration({ repositoryRoot = defaultRepositoryRoot, keep = false, env = process.env, failAt = env.P1_ACCEPTANCE_FAIL_AT, checks, setup, announce, ownershipWriter = writeOwnership, } = {}) { if (integrationOwner) throw new Error("P1 acceptance integration is already active"); const acquisitionToken = Symbol("p1-integration-owner"); integrationOwner = acquisitionToken; const savedEnv = { ...process.env }; let installedEnv; let run; let ctx; let results = []; let fatal; let auditFailed = false; try { try { run = await createOwnedRun({ repositoryRoot }); ctx = { run, repositoryRoot, forbiddenValues: [], expectedGitRepositories: [], services: [], listenerClosureEvidence: [], originalFetch: globalThis.fetch, }; commandEventSink = []; const selectedSetup = setup ?? (checks === undefined ? setupContext : undefined); if (selectedSetup) { const configured = await selectedSetup(run, repositoryRoot, env, ctx); if (configured && configured !== ctx) Object.assign(ctx, configured); } if (checks === undefined) { if (!ctx.env || !ctx.executables) throw new Error("acceptance setup returned no bounded environment"); installedEnv = { ...ctx.env }; replaceProcessEnvironment(installedEnv); ctx.networkGuard = installProductionSurfaceGuard({ ...ctx.executables, runRoot: run.root, environment: installedEnv, originalFetch: ctx.originalFetch, }); checks = productionChecks(ctx); } else if (ctx.env) { installedEnv = { ...ctx.env }; replaceProcessEnvironment(installedEnv); } results = await executeChecks({ checks, failAt }); } catch (error) { fatal = error; if (run) results = completeFailedResults(results); } finally { if (ctx?.services) { for (const service of [...ctx.services].reverse()) { const actualPort = Number(new URL(service.baseUrl).port); let closed = false; let closeError; try { await service.app.close(); closed = await listenerRefuses(service.baseUrl); if (!closed) closeError = new Error("listener still accepts connections after close"); } catch (error) { closeError = error; } const state = closed ? "closed" : "close_failed"; ctx.listenerClosureEvidence.push({ name: service.name, host: "127.0.0.1", actualPort, state, listenerRefusedConnection: closed }); try { await ownershipWriter(run, { name: service.name, kind: "fastify", host: "127.0.0.1", requestedPort: 0, actualPort, pid: process.pid, state, }); } catch (error) { closeError ??= error; } if (closeError) { fatal ??= closeError; auditFailed = true; } } } try { ctx?.networkGuard?.restore(); } catch (error) { fatal ??= error; auditFailed = true; } if (installedEnv && !environmentMatches(installedEnv)) { fatal ??= new Error("acceptance environment ownership changed"); auditFailed = true; } try { replaceProcessEnvironment(savedEnv); } catch (error) { fatal ??= error; auditFailed = true; } if (!environmentMatches(savedEnv)) { fatal ??= new Error("acceptance environment restoration failed"); auditFailed = true; } } if (!run) throw fatal; results = completeFailedResults(results); const commandEvents = commandEventSink ?? []; commandEventSink = undefined; activeCommandCheckId = undefined; try { assertUniqueResultArtifacts(results); const finalOwnershipBytes = await readFile(join(run.root, "ownership.json")); const finalOwnership = await readAndValidateOwnership({ repositoryRoot, runRoot: run.root, expectedNonce: run.nonce }); if (ctx.services.length > 0) { assert(ctx.listenerClosureEvidence.length === ctx.services.length && ctx.listenerClosureEvidence.every(({ state, listenerRefusedConnection }) => state === "closed" && listenerRefusedConnection), "final listener closure evidence is incomplete"); } const finalOwnershipArtifact = await evidence(run, "logs/final-ownership.json", { ownershipSha256: sha256(finalOwnershipBytes), listeners: finalOwnership.listeners, listenerRefusalChecks: ctx.listenerClosureEvidence, }, ctx.forbiddenValues); attachResultArtifact(results, "ownership", finalOwnershipArtifact); const commandArtifact = await evidence(run, "logs/command-events.json", { eventCount: commandEvents.length, events: commandEvents }, ctx.forbiddenValues); attachResultArtifact(results, "preflight", commandArtifact); if (ctx.networkGuard) { const executablePolicy = {}; 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, gitGlobalConfigDisabled: true, gitSystemConfigDisabled: true, gitSystemAttributesDisabled: true, gitPromptsHelpersSigningDisabled: true, gitLocalConfigAttributesHooksValidatedBeforeInvocation: true, gitFixedConfigPrefix: ctx.networkGuard.gitPolicy.fixedConfig, gitOwnedEmptyHooksPath: ctx.networkGuard.gitPolicy.hooksPath, gitAllowedProtocol: "file", maxOutputBytes: MAX_OUTPUT, maxTimeoutMs: 300_000, }, eventCount: ctx.networkGuard.events.length, events: ctx.networkGuard.events, }, ctx.forbiddenValues); attachResultArtifact(results, "no_p1_scope_artifacts", childArtifact); } if (ctx.gitTracePath) { const gitTraceBytes = await readFile(ctx.gitTracePath); for (const line of gitTraceBytes.toString("utf8").split("\n").filter(Boolean)) JSON.parse(line); attachResultArtifact(results, "no_p1_scope_artifacts", await fileArtifact(run.root, relative(run.root, ctx.gitTracePath))); } assertUniqueResultArtifacts(results); if (ctx.executables && !ctx.gitSecretScanFrozen) throw new Error("expected Git secret scan was not frozen inside secret_scan"); } catch { auditFailed = true; } let report = { schemaVersion: 1, runId: run.runId, startedAt: run.startedAt, finishedAt: nowIso(), command: `p1-acceptance integration${keep ? " --keep" : ""}`, overall: !fatal && !auditFailed && deriveOverall(results) === "PASS" ? "PASS" : "FAIL", checks: results, }; let bytes; try { bytes = reportBytes(report); const findings = await scanSecrets({ runRoot: run.root, forbiddenValues: ctx.forbiddenValues, expectedGitRepositories: [], virtualFiles: [{ path: "report.json", bytes: bytes.json }, { path: "report.md", bytes: bytes.markdown }], }); if (findings.length > 0) throw new Error("final filesystem or virtual secret scan failed"); await verifyDeclaredArtifacts(run.root, results); } catch { auditFailed = true; } if (auditFailed) { report = minimalFailClosedReport(run, keep); bytes = reportBytes(report); if (containsAny(bytes.json, ctx.forbiddenValues) || containsAny(bytes.markdown, ctx.forbiddenValues)) { throw new Error("sanitized fail-closed report unexpectedly contains a forbidden value"); } } await atomicWrite(join(run.root, "report.json"), bytes.json); await atomicWrite(join(run.root, "report.md"), bytes.markdown); const finalSuccess = report.overall === "PASS"; if (announce) await announce({ report, runRoot: run.root, keep }); const removed = await finalizeOwnedRun({ run, success: finalSuccess, keep }); return { exitCode: finalSuccess ? 0 : 1, runRoot: run.root, retained: !removed, report }; } finally { commandEventSink = undefined; activeCommandCheckId = undefined; if (integrationOwner === acquisitionToken) integrationOwner = undefined; else if (integrationOwner !== undefined) throw new Error("P1 acceptance integration ownership changed"); } } export async function main(argv = process.argv.slice(2), env = process.env) { if (argv.length < 1 || argv[0] !== "integration" || argv.length > 2 || (argv.length === 2 && argv[1] !== "--keep")) { console.error("usage: p1-acceptance integration [--keep]"); return 2; } try { const result = await runIntegration({ repositoryRoot: defaultRepositoryRoot, keep: argv.includes("--keep"), env, announce: async ({ report, runRoot, keep }) => { console.log(`automated integration: ${report.overall}`); console.log("manual acceptance: PENDING"); if (keep || report.overall !== "PASS") console.log(runRoot); }, }); return result.exitCode; } catch (error) { console.error("P1 acceptance failed before owning a reportable run."); return 1; } } if (resolve(process.argv[1] ?? "") === modulePath) process.exitCode = await main();