Files
ModelForge/scripts/validate-public-source.mjs
T

95 lines
3.6 KiB
JavaScript

#!/usr/bin/env node
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
function fail(message) {
console.error(message);
process.exit(1);
}
function walk(directory, prefix = "") {
const files = [];
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
if (!prefix && entry.name === ".git") continue;
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
const absolutePath = path.join(directory, entry.name);
if (entry.isSymbolicLink()) fail(`Symbolic link present in public export: ${relativePath}`);
if (entry.isDirectory()) files.push(...walk(absolutePath, relativePath));
else if (entry.isFile()) files.push(relativePath);
else fail(`Non-regular entry present in public export: ${relativePath}`);
}
return files;
}
const required = [
"README.md", "SECURITY.md", "CONTRIBUTING.md", "LICENSE", "VERSION",
"PUBLIC_SOURCE_EXPORT.md", "PUBLIC_SOURCE_MANIFEST.json", "docker-compose.yml",
"backend/pyproject.toml", "frontend/package.json", "node-agent/pyproject.toml",
"runtime-worker/pyproject.toml"
];
const missing = required.filter((entry) => !fs.existsSync(path.join(process.cwd(), entry)));
if (missing.length > 0) {
fail(`Missing public source files: ${missing.join(", ")}`);
}
for (const privatePath of [".claude", ".agents", ".codex", "artifacts", "docs/quality"]) {
if (fs.existsSync(path.join(process.cwd(), privatePath))) {
fail(`Private-only path present in public export: ${privatePath}`);
}
}
const manifest = JSON.parse(fs.readFileSync("PUBLIC_SOURCE_MANIFEST.json", "utf8"));
if (
manifest.schemaVersion !== 1 ||
typeof manifest.sourceRevision !== "string" ||
!/^[0-9a-f]{40}$/u.test(manifest.sourceRevision) ||
!Array.isArray(manifest.files) ||
manifest.files.length === 0
) {
fail("PUBLIC_SOURCE_MANIFEST.json has an invalid schema or source revision.");
}
const declared = new Set();
for (const entry of manifest.files) {
if (
!entry ||
typeof entry.path !== "string" ||
entry.path !== path.posix.normalize(entry.path) ||
path.posix.isAbsolute(entry.path) ||
entry.path.startsWith("../") ||
entry.path.includes("\\") ||
!Number.isSafeInteger(entry.bytes) ||
entry.bytes < 0 ||
typeof entry.sha256 !== "string" ||
!/^[0-9a-f]{64}$/u.test(entry.sha256)
) {
fail("PUBLIC_SOURCE_MANIFEST.json contains an invalid file entry.");
}
const collisionKey = entry.path.toLocaleLowerCase("en-US");
if (declared.has(collisionKey)) fail(`Duplicate manifest path: ${entry.path}`);
declared.add(collisionKey);
const absolutePath = path.join(process.cwd(), ...entry.path.split("/"));
if (!fs.existsSync(absolutePath)) fail(`Manifest file is missing: ${entry.path}`);
const stat = fs.lstatSync(absolutePath);
if (!stat.isFile() || stat.isSymbolicLink()) {
fail(`Manifest path is not a regular file: ${entry.path}`);
}
const bytes = fs.readFileSync(absolutePath);
const digest = crypto.createHash("sha256").update(bytes).digest("hex");
if (bytes.length !== entry.bytes) fail(`Manifest size mismatch: ${entry.path}`);
if (digest !== entry.sha256) fail(`Manifest hash mismatch: ${entry.path}`);
}
const generated = new Set(["public_source_export.md", "public_source_manifest.json"]);
const unexpected = walk(process.cwd()).filter(
(entry) => !declared.has(entry.toLocaleLowerCase("en-US")) &&
!generated.has(entry.toLocaleLowerCase("en-US"))
);
if (unexpected.length > 0) fail(`Unexpected public source files: ${unexpected.join(", ")}`);
console.log(`Public source integrity passed (${manifest.files.length} allowlisted files).`);