Files
ThothII/backend/scripts/verify-workspace-descriptor-files.mjs
T

379 lines
16 KiB
JavaScript
Executable File

#!/usr/bin/env node
import { createHash } from "node:crypto";
import { lstat, readFile, realpath } from "node:fs/promises";
import { isAbsolute, relative, resolve, sep } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { isMap, isScalar, parseAllDocuments } from "yaml";
import { extractBashDocuments } from "./bash-heredoc.mjs";
import { validatePythonRevisionStates, validateRevisionState } from "./revision-state-policy.mjs";
import { parseWorkspaceYaml } from "../dist/workspaces/schema.js";
const scriptPath = fileURLToPath(import.meta.url);
const allowedKinds = new Set(["policy_text", "workspace_descriptor", "deployment_script"]);
// Exact-content trust exceptions. Each digest covers the raw UTF-8 bytes from the
// opener line through the closer line (including physical line endings). These
// blocks are reviewed non-workspace runtime/config generation, not semantic proof.
const reviewedExpandableBlocks = new Map([
["scripts/test-dwh-auth-nginx-integration.sh", [
{ sha256: "ead57234ad3520b5c7d4262b772957cbc7b9589da4f35fb17b160f948eb2ac7b", rationale: "Generates the reviewed isolated Nginx integration configuration." },
]],
["scripts/test-install-tht.sh", [
{ sha256: "37f18ce7ce93cb8b84f3b3708462cc16d50fdc7bab22836c382dbacf8382f05f", rationale: "Generates the reviewed synthetic tht installer artifact." },
]],
["scripts/test-server-pi-state-topology.sh", [
{ sha256: "6ae9567db53d6cd45a2c19c98acaf45f382450b157ea7d6f6d35125f68c50947", rationale: "Generates the isolated server topology test environment, including its installation descriptor and authentication configuration root." },
]],
["scripts/test-vector-backup-restore-safety.sh", [
{ sha256: "40b8a10a3c06aaa98e324fbf688b7d1f5cead330d7ba7eef98e06256d412a85a", rationale: "Generates the reviewed restore safety manifest." },
]],
["scripts/test-windows-clone-contract.ps1", [
{ sha256: "3204f772d33cad42bcac99191507051aefb2c91d2935bec6698b956e44f9bf45", rationale: "Generates reviewed Windows clone test configuration with its authentication configuration root." },
{ sha256: "f4814d842a7502b7ef30fd6b224d5cb17b0ffd6fb2367c41c49ac16587536d93", rationale: "Same reviewed block in the repository-required CRLF checkout representation." },
{ sha256: "6f25ce3b58cea47b74fe9319ed917d8089a2fb334bc0d469daa7e1f10865d870", rationale: "Generates the reviewed Windows Compose override for the canonical service topology." },
{ sha256: "45a3cf19f7ce697b858b63d27a4edc7fefa2414d0408e7b6d72a65c86d314f5b", rationale: "Same reviewed Compose override in the repository-required CRLF checkout representation." },
{ sha256: "5d0d1a3fc45e99b3aacaf4ee5dd09a6bee1937784375dfe4bcfaa4ae32cfb9de", rationale: "Generates reviewed Windows clone test configuration." },
{ sha256: "b903e5dae953ae1372f1a5276f12a92ed3dd632b897f3afe5e00c646d90a1b42", rationale: "Same reviewed block in the repository-required CRLF checkout representation." },
]],
["scripts/unified-deployment-smoke.sh", [
{ sha256: "b6c0826151b2c8b955399d1abf5b691cc8fe6b6454b17da000dde7ba3bc55d2d", rationale: "Generates the reviewed local Task 13 Compose override with normalized catalog mounts." },
{ sha256: "24f69d12b8554aa2bebba455be99fde3e60743eef5a40fa2ef5b29397a477c03", rationale: "Generates the reviewed local Task 13 installation descriptor with its model catalog." },
{ sha256: "526006fa6d48a8080b3834723630c64de5005a67243e944ebf1da15212b4d654", rationale: "Generates the reviewed server Task 13 Compose override." },
{ sha256: "406ccead1967f642225c946fc4a23fe5b019c9764cc5153e1125876ade16ec90", rationale: "Generates the reviewed projected-auth server Task 13 installation descriptor with its model catalog." },
]],
["scripts/vector-backup.sh", [
{ sha256: "571899db49dfdcec8107fbe1e0a86a61e7581979d3c4c248c20546843e275bcf", rationale: "Generates the reviewed backup manifest inside the helper command." },
]],
["scripts/vector-restore.sh", [
{ sha256: "f04d872e556a7323583c6e620b25814fb6a8e2568a9a555623978185b473a49d", rationale: "Feeds reviewed parsed manifest values to read loops." },
{ sha256: "c6053ed44abae71ae4821b68f9a513f8070947350e30d89ae0f65bf4a48f66fd", rationale: "Feeds reviewed parsed manifest values to read loops." },
]],
]);
function blockDigest(rawBlock) {
return createHash("sha256").update(rawBlock, "utf8").digest("hex");
}
function reviewedExpandableBlock(path, rawBlock) {
const digest = blockDigest(rawBlock);
return (reviewedExpandableBlocks.get(path) ?? []).some((review) => review.sha256 === digest);
}
function hasAmbiguousExpansion(source, path) {
const powershell = path.endsWith(".ps1");
for (let index = 0; index < source.length; index += 1) {
const character = source[index];
if (powershell && character === "`") {
index += 1;
continue;
}
if (!powershell && character === "\\") {
index += 1;
continue;
}
if (character === "$" || (!powershell && character === "`")) return true;
}
return false;
}
function physicalLines(source) {
const rawLines = source.match(/[^\n]*\n|[^\n]+$/gu) ?? [];
if (rawLines.length === 0) rawLines.push("");
return rawLines.map((raw) => ({ raw, text: raw.replace(/\n$/u, "").replace(/\r$/u, "") }));
}
const prescribedSymbols = [
"WorkspaceV1", "WorkspaceV2", "DeprecatedV2Descriptor", "LegacyMigrationResult",
"LegacyMigrationOptions", "WorkspaceV2MigrationInput", "migrateLegacyWorkspace",
"writeMigratedWorkspace", "migrateWorkspaceV1ToV2", "migrateWorkspaceV2ToV3",
];
const migrationMarkers = ["migration_required", "deprecated-v2-descriptor", "migrate-legacy", "migrate-v2-qdrant"];
function isPolicyImplementationException(label, category) {
const implementations = new Set([
"scripts/verify-schema-v3-only.sh",
"scripts/test-verify-schema-v3-only.sh",
"backend/scripts/verify-workspace-descriptor-files.mjs",
"backend/scripts/verify-workspace-descriptor-files.test.mjs",
"backend/scripts/revision-state-policy.mjs",
"backend/scripts/revision-state-policy.test.mjs",
"backend/scripts/bash-heredoc.mjs",
"backend/scripts/revision_state_policy.py",
"backend/scripts/test_revision_state_policy.py",
]);
if (implementations.has(label)) return true;
if (category === "migration-marker" && new Set([
"backend/src/workspaces/schema.ts",
"scripts/workspace_descriptor_doc_contract.py",
"scripts/test_workspace_descriptor_doc_contract.py",
"backend/scripts/clean-dist.test.mjs",
]).has(label)) return true;
return false;
}
function validatePolicySource(source, label) {
if (!isPolicyImplementationException(label, "prescribed-symbol")) {
for (const symbol of prescribedSymbols) {
if (source.toLowerCase().includes(symbol.toLowerCase())) throw new Error(`${label}: forbidden prescribed-symbol substring: ${symbol}`);
}
}
if (!isPolicyImplementationException(label, "migration-marker")) {
for (const marker of migrationMarkers) {
if (source.toLowerCase().includes(marker.toLowerCase())) throw new Error(`${label}: forbidden migration-marker substring: ${marker}`);
}
}
if (!isPolicyImplementationException(label, "legacy-workspace")) {
for (const match of source.matchAll(/legacyworkspace/giu)) {
if (match[0] !== "legacyWorkspace") throw new Error(`${label}: forbidden legacy-workspace spelling: ${match[0]}`);
}
}
if (!/\.pyw?$/iu.test(label) && !isPolicyImplementationException(label, "revision-state")) validateRevisionState(source, label);
}
function documentShape(document) {
const shape = { workspacePresent: false, workspaceMapping: false };
if (!isMap(document.contents)) return shape;
for (const pair of document.contents.items) {
if (!isScalar(pair.key)) continue;
if (pair.key.value === "workspace") {
shape.workspacePresent = true;
if (isMap(pair.value)) shape.workspaceMapping = true;
}
}
return shape;
}
function documents(source) {
try {
return parseAllDocuments(source, { uniqueKeys: true });
} catch (error) {
throw new Error(`YAML parser failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
function validateWorkspaceSource(source, label, { requireWorkspace, expandable = false, path, rawBlock }) {
const parsed = documents(source);
const shapes = parsed.map(documentShape);
if (requireWorkspace) {
if (!shapes.some((shape) => shape.workspacePresent)) {
throw new Error(`${label}: expected a top-level workspace mapping`);
}
if (!shapes.some((shape) => shape.workspaceMapping)) {
throw new Error(`${label}: top-level workspace must be a mapping`);
}
} else {
if (expandable && hasAmbiguousExpansion(source, path) && !reviewedExpandableBlock(path, rawBlock)) {
throw new Error(`${label}: expandable block interpolation is not in the exact-content reviewed allowlist`);
}
if (shapes.some((shape) => shape.workspaceMapping)) {
throw new Error(`${label}: embedded workspace descriptor is forbidden; use a tracked workspace fixture`);
}
return false;
}
try {
parseWorkspaceYaml(source);
} catch (error) {
throw new Error(`${label}: workspace descriptor is not valid schema v4: ${error instanceof Error ? error.message : String(error)}`);
}
return true;
}
function deploymentScriptDialect(path) {
if (path.endsWith(".sh")) return "bash";
if (path.endsWith(".ps1")) return "powershell";
throw new Error(`${path}: unknown deployment script dialect`);
}
function powerShellHereStringOpener(line, state) {
let quote = null;
for (let index = 0; index < line.length; index += 1) {
if (state.blockComment) {
const close = line.indexOf("#>", index);
if (close < 0) return null;
state.blockComment = false;
index = close + 1;
continue;
}
const character = line[index];
if (quote === null && character === "`") {
index += 1;
continue;
}
if (quote === "'") {
if (character === "'" && line[index + 1] === "'") index += 1;
else if (character === "'") quote = null;
continue;
}
if (quote === '"') {
if (character === "`") index += 1;
else if (character === '"') quote = null;
continue;
}
if (character === "#") return null;
if (character === "<" && line[index + 1] === "#") {
state.blockComment = true;
index += 1;
continue;
}
if (character === "@" && (line[index + 1] === "'" || line[index + 1] === '"') && /^[ \t]*$/u.test(line.slice(index + 2))) return line[index + 1];
if (character === "'" || character === '"') quote = character;
}
return null;
}
function extractPowerShellDocuments(source, label) {
const records = physicalLines(source);
const lines = records.map((record) => record.text);
const extracted = [];
const state = { blockComment: false };
for (let index = 0; index < lines.length; index += 1) {
const quote = powerShellHereStringOpener(lines[index], state);
if (quote === null) continue;
const delimiter = `${quote}@`;
const opener = index;
const body = [];
const start = index + 2;
let closed = false;
for (index += 1; index < lines.length; index += 1) {
if (lines[index].trimEnd() === delimiter) {
closed = true;
break;
}
body.push(lines[index]);
}
extracted.push({
source: `${body.join("\n")}\n`,
label: `${label}:${start} PowerShell here-string${closed ? "" : " (unclosed)"}`,
expandable: quote === '"',
path: label,
rawBlock: records.slice(opener, Math.min(index + 1, records.length)).map((record) => record.raw).join(""),
});
}
return extracted;
}
export function extractScriptDocuments(source, label = "deployment script") {
const dialect = deploymentScriptDialect(label);
if (dialect === "bash") return extractBashDocuments(source, label);
return extractPowerShellDocuments(source, label);
}
async function safeFile(root, path) {
if (typeof path !== "string" || path.length === 0 || isAbsolute(path) || path.includes("\\")) {
throw new Error(`unsafe verifier path: ${JSON.stringify(path)}`);
}
const segments = path.split("/");
if (segments.some((segment) => segment === "" || segment === "." || segment === "..")) {
throw new Error(`unsafe verifier path: ${JSON.stringify(path)}`);
}
const absolute = resolve(root, ...segments);
const fromRoot = relative(root, absolute);
if (fromRoot.startsWith(`..${sep}`) || fromRoot === ".." || isAbsolute(fromRoot)) {
throw new Error(`verifier path escapes root: ${JSON.stringify(path)}`);
}
const entry = await lstat(absolute);
if (!entry.isFile() || entry.isSymbolicLink()) {
throw new Error(`verifier input is not a regular file: ${path}`);
}
const canonical = await realpath(absolute);
const canonicalRelative = relative(root, canonical);
if (canonicalRelative.startsWith(`..${sep}`) || canonicalRelative === ".." || isAbsolute(canonicalRelative)) {
throw new Error(`verifier input resolves outside root: ${path}`);
}
return absolute;
}
export async function verifyEntries({ root, entries }) {
const canonicalRoot = await realpath(root);
const seen = new Set();
const pythonPolicies = [];
for (const entry of entries) {
if (!entry || !allowedKinds.has(entry.kind) || typeof entry.path !== "string") {
throw new Error("workspace verifier manifest contains an invalid entry");
}
const identity = `${entry.kind}\0${entry.path}`;
if (seen.has(identity)) throw new Error(`workspace verifier manifest duplicates: ${entry.path}`);
seen.add(identity);
const absolute = await safeFile(canonicalRoot, entry.path);
const bytes = await readFile(absolute);
let source;
try {
source = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
} catch {
throw new Error(`${entry.path}: input is not valid UTF-8`);
}
if (source.includes("\0")) throw new Error(`${entry.path}: NUL byte is forbidden`);
if (entry.kind === "policy_text") {
validatePolicySource(source, entry.path);
if (/\.pyw?$/iu.test(entry.path) && !isPolicyImplementationException(entry.path, "revision-state")) {
pythonPolicies.push({ label: entry.path, source });
}
continue;
}
if (entry.kind === "workspace_descriptor") {
validateWorkspaceSource(source, entry.path, { requireWorkspace: true });
continue;
}
for (const candidate of extractScriptDocuments(source, entry.path)) {
validateWorkspaceSource(candidate.source, candidate.label, {
requireWorkspace: false,
expandable: candidate.expandable,
path: entry.path,
rawBlock: candidate.rawBlock,
});
}
}
validatePythonRevisionStates(pythonPolicies);
}
export function decodeManifest(bytes) {
const fields = bytes.toString("utf8").split("\0");
if (fields.at(-1) !== "") throw new Error("workspace verifier manifest is not NUL-terminated");
fields.pop();
if (fields.length % 2 !== 0) throw new Error("workspace verifier manifest has an incomplete record");
const entries = [];
for (let index = 0; index < fields.length; index += 2) {
entries.push({ kind: fields[index], path: fields[index + 1] });
}
return entries;
}
function cliArguments(argv) {
let root;
let manifest;
for (let index = 0; index < argv.length; index += 1) {
const option = argv[index];
const value = argv[index + 1];
if ((option === "--root" || option === "--manifest") && value !== undefined) {
if (option === "--root" && root === undefined) root = value;
else if (option === "--manifest" && manifest === undefined) manifest = value;
else throw new Error(`duplicate or invalid option: ${option}`);
index += 1;
} else {
throw new Error(`unknown or incomplete option: ${option}`);
}
}
if (root === undefined || manifest === undefined) {
throw new Error("usage: verify-workspace-descriptor-files.mjs --root ROOT --manifest NUL_FILE");
}
return { root, manifest };
}
async function main(argv) {
const { root, manifest } = cliArguments(argv);
const manifestEntry = await lstat(manifest);
if (!manifestEntry.isFile() || manifestEntry.isSymbolicLink()) {
throw new Error("workspace verifier manifest is not a regular file");
}
const entries = decodeManifest(await readFile(manifest));
await verifyEntries({ root, entries });
}
if (process.argv[1] && pathToFileURL(resolve(process.argv[1])).href === import.meta.url) {
main(process.argv.slice(2)).catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exitCode = 1;
});
}