Use exact discovered test identities, production MCP sessions and byte-exact setup backups. Add CLI signal cleanup coverage and independent Ghidra, Go and firmware verification assertions; prune redundant test configuration and preserve platform limits.
574 lines
17 KiB
JavaScript
574 lines
17 KiB
JavaScript
#!/usr/bin/env node
|
|
import assert from "node:assert/strict";
|
|
import { isUtf8 } from "node:buffer";
|
|
import { execFile } from "node:child_process";
|
|
import { createHash } from "node:crypto";
|
|
import { lstat, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
import { isAbsolute, join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { promisify } from "node:util";
|
|
import { Client } from "@modelcontextprotocol/client";
|
|
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
|
import { parseEvidence } from "../dist/domain/evidence.js";
|
|
import { goBinarySchema } from "../dist/domain/go/goBinary.js";
|
|
import { PrivateRuntimeRoot } from "../dist/process/PrivateRuntimeRoot.js";
|
|
import { mcpTextValue } from "./lib/mcp-verifier-results.mjs";
|
|
import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs";
|
|
|
|
const execute = promisify(execFile);
|
|
const compiler = process.env.REA_VERIFY_GO_COMMAND ?? "go";
|
|
if (process.env.REA_VERIFY_GO_COMMAND !== undefined && !isAbsolute(compiler))
|
|
throw new Error(
|
|
"REA_VERIFY_GO_COMMAND must be an absolute Go compiler path.",
|
|
);
|
|
let compilerVersion = null;
|
|
const run = createVerifierRun();
|
|
const root = await PrivateRuntimeRoot.create({ prefix: "rea-go-verifier-" });
|
|
// Keep Go's directory enumeration below the private root's Windows ownership lease.
|
|
const sourceRoot = join(root.path, "source");
|
|
const entrypoint = fileURLToPath(new URL("./rea.mjs", import.meta.url));
|
|
const environment = Object.fromEntries(
|
|
Object.entries(process.env).filter(([, value]) => typeof value === "string"),
|
|
);
|
|
delete environment.HOPPER_TARGET_PATH;
|
|
const goEnvironment = {
|
|
...environment,
|
|
CGO_ENABLED: "0",
|
|
GOCACHEPROG: "",
|
|
GO111MODULE: "on",
|
|
GOENV: "off",
|
|
GOFLAGS: "",
|
|
GOMAXPROCS: "2",
|
|
GOTOOLCHAIN: "local",
|
|
GOWORK: "off",
|
|
GOPROXY: "off",
|
|
GOSUMDB: "off",
|
|
GOPATH: join(root.path, "gopath"),
|
|
GOCACHE: join(root.path, "cache"),
|
|
GOMODCACHE: join(root.path, "modules"),
|
|
GOTMPDIR: join(root.path, "tmp"),
|
|
// Go's own verifier seam isolates telemetry; no global configuration is changed.
|
|
TEST_TELEMETRY_DIR: join(root.path, "telemetry"),
|
|
};
|
|
const client = new Client({ name: "go-binary-verifier", version: "1" });
|
|
const transport = new StdioClientTransport({
|
|
command: process.execPath,
|
|
args: [entrypoint, "mcp"],
|
|
env: environment,
|
|
stderr: "pipe",
|
|
});
|
|
const targets = [
|
|
["linux", "amd64", "elf", 64, "little"],
|
|
["linux", "386", "elf", 32, "little"],
|
|
["linux", "arm64", "elf", 64, "little"],
|
|
["linux", "ppc64", "elf", 64, "big"],
|
|
["linux", "mips", "elf", 32, "big"],
|
|
["windows", "amd64", "pe", 64, "little"],
|
|
["windows", "386", "pe", 32, "little"],
|
|
["darwin", "amd64", "macho", 64, "little"],
|
|
["darwin", "arm64", "macho", 64, "little"],
|
|
];
|
|
const verified = [];
|
|
const omittedCoverage = [];
|
|
const failures = [];
|
|
try {
|
|
await prepareFixture();
|
|
try {
|
|
compilerVersion = (
|
|
await execute(compiler, ["version"], {
|
|
cwd: root.path,
|
|
env: goEnvironment,
|
|
timeout: 10_000,
|
|
})
|
|
).stdout.trim();
|
|
} catch (cause) {
|
|
throw new Error(
|
|
"verify:go:binary requires Go on PATH or absolute REA_VERIFY_GO_COMMAND; Go is only a fixture producer, not a REA runtime dependency.",
|
|
{ cause },
|
|
);
|
|
}
|
|
const host = (await go(["env", "GOHOSTOS", "GOHOSTARCH"])).stdout
|
|
.trim()
|
|
.split("\n");
|
|
const oracle = join(
|
|
root.path,
|
|
host[0] === "windows" ? "buildinfo-oracle.exe" : "buildinfo-oracle",
|
|
);
|
|
await go(
|
|
["build", "-trimpath", "-buildvcs=false", "-o", oracle, "./oracle"],
|
|
{ GOOS: host[0], GOARCH: host[1] },
|
|
);
|
|
await client.connect(transport);
|
|
for (const target of targets) {
|
|
const [os, arch] = target;
|
|
const path = join(
|
|
root.path,
|
|
`${os}-${arch}${os === "windows" ? ".exe" : ""}`,
|
|
);
|
|
await go(
|
|
[
|
|
"build",
|
|
"-trimpath",
|
|
"-buildvcs=false",
|
|
"-ldflags=-s -w",
|
|
"-o",
|
|
path,
|
|
".",
|
|
],
|
|
{ GOOS: os, GOARCH: arch },
|
|
);
|
|
const strippedInfo = await verify(path, target, oracle, null, "stripped");
|
|
if (os === "linux" && arch === "amd64") {
|
|
const sectionless = join(root.path, "linux-amd64-sectionless");
|
|
const bytes = Buffer.from(await readFile(path));
|
|
bytes.fill(0, 40, 48); // ELF64 e_shoff.
|
|
bytes.fill(0, 58, 64); // e_shentsize, e_shnum and e_shstrndx.
|
|
await writeFile(sectionless, bytes);
|
|
const sectionlessInfo = await verify(
|
|
sectionless,
|
|
target,
|
|
oracle,
|
|
null,
|
|
"sectionless",
|
|
path,
|
|
);
|
|
assert.deepEqual(sectionlessInfo, strippedInfo);
|
|
|
|
const pie = join(root.path, "linux-amd64-pie");
|
|
await go(
|
|
[
|
|
"build",
|
|
"-trimpath",
|
|
"-buildvcs=false",
|
|
"-ldflags=-s -w",
|
|
"-buildmode=pie",
|
|
"-o",
|
|
pie,
|
|
".",
|
|
],
|
|
{ GOOS: os, GOARCH: arch },
|
|
);
|
|
const originalInfo = await verify(pie, target, oracle, null, "pie");
|
|
const pieBytes = Buffer.from(await readFile(pie));
|
|
pieBytes.fill(0, 40, 48);
|
|
pieBytes.fill(0, 58, 64);
|
|
const pieSectionless = `${pie}-sectionless`;
|
|
await writeFile(pieSectionless, pieBytes);
|
|
// Keep the producer image as the independent oracle. Removing section
|
|
// headers must preserve every metadata field and original source span.
|
|
const retainedInfo = await verify(
|
|
pieSectionless,
|
|
target,
|
|
oracle,
|
|
null,
|
|
"sectionless-pie",
|
|
pie,
|
|
);
|
|
assert.deepEqual(retainedInfo, originalInfo);
|
|
}
|
|
}
|
|
for (const target of targets.filter(([os]) => os === "darwin")) {
|
|
const [os, arch] = target;
|
|
const path = join(root.path, `${os}-${arch}-unstripped`);
|
|
await go(["build", "-trimpath", "-buildvcs=false", "-o", path, "."], {
|
|
GOOS: os,
|
|
GOARCH: arch,
|
|
});
|
|
await verify(path, target, oracle, null, "unstripped");
|
|
}
|
|
if (process.platform === "win32")
|
|
omittedCoverage.push(
|
|
"Compiler-produced non-UTF-8 argv strings were not verified on Windows; Windows argv cannot preserve arbitrary byte arguments.",
|
|
);
|
|
else {
|
|
const |