Files
openrig/scripts/check-internal-leak-guard.test.mjs
T
Mike Schwarz c4de1b2c38 build: read leak-guard rules from an optional local file (#2137)
The leak guard's term lists now come from an optional local rules file instead of a file tracked in the repository. Set `OPENRIG_LEAK_GUARD_RULES` (or pass `--rules`) to an absolute path, and every reader uses it through one loader: the leak guard, `build-package.sh`, the context packs, the skill mirror and the control-plane generator. The publishing rules, the frontmatter keys and the internal-section fence stay in the repository unchanged, so mirrored skills are the same.

Without the file, a contributor's `npm run build:package` still works and prints that the term checks were skipped. The release substance gate refuses to run without a rules file, so a release can't pass with the checks silently off. The tracked rules file is removed, and the tests write their own fixture rules.

16 files, +227 -244.
2026-10-26 18:46:46 -07:00

353 lines
12 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import { execFileSync, spawnSync } from "node:child_process";
import {
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { dirname, join, resolve } from "node:path";
import { tmpdir } from "node:os";
const GUARD = resolve("scripts/check-internal-leak-guard.mjs");
test("full mode scans tracked files and fails loudly on a planted leak", () => {
requireGuard();
withRepo((repo, rules) => {
write(join(repo, "README.md"), "founder-only detail\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "tracked leak");
const result = runGuard(repo, rules, "--mode", "full");
assert.notEqual(result.status, 0);
assert.match(result.stderr, /README\.md/);
assert.match(result.stderr, /founder/);
assert.match(result.stderr, /line 1/i);
assert.match(result.stderr, /sidecar|fence|genericize|host-only/i);
});
});
test("full mode passes a clean tracked tree and ignores untracked files", () => {
requireGuard();
withRepo((repo, rules) => {
write(join(repo, "README.md"), "public detail\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "clean");
write(join(repo, "untracked.md"), "founder\n");
const result = runGuard(repo, rules, "--mode", "full");
assert.equal(result.status, 0, result.stderr);
});
});
test("without a rules file the term checks are skipped with a note; the file the environment names turns them on", () => {
requireGuard();
withRepo((repo, rules) => {
writeFileSync(rules, JSON.stringify({ ...fixtureRules(), charged_terms: ["private-term"] }));
write(join(repo, "README.md"), "a private-term detail\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "tracked term");
const env = { ...process.env };
delete env.OPENRIG_LEAK_GUARD_RULES;
const guard = (extra) => spawnSync(process.execPath, [GUARD, "--repo", repo, "--mode", "full"],
{ cwd: repo, encoding: "utf8", env: { ...env, ...extra } });
const skipped = guard({});
assert.equal(skipped.status, 0, skipped.stderr);
assert.match(skipped.stderr, /No leak-guard rules file .*the term checks are skipped/);
const named = guard({ OPENRIG_LEAK_GUARD_RULES: rules });
assert.notEqual(named.status, 0);
assert.match(named.stderr, /README\.md/);
assert.match(named.stderr, /private-term/);
const missing = guard({ OPENRIG_LEAK_GUARD_RULES: join(repo, "absent.json") });
assert.notEqual(missing.status, 0);
assert.match(missing.stderr, /Rules file not found/);
});
});
test("optional exact-line rules reach the full guard and malformed values are refused", () => {
withRepo((repo, rulesPath) => {
const line = "Generic operator-agent@<seat> example";
const rules = { ...fixtureRules(), allowed_context_lines: [line] };
writeFileSync(rulesPath, JSON.stringify(rules));
write(join(repo, "README.md"), `${line}\n`);
git(repo, "add", ".");
git(repo, "commit", "-m", "generic example");
const result = runGuard(repo, rulesPath, "--mode", "full");
assert.equal(result.status, 0, result.stderr);
for (const invalid of [null, "text", [42], [""], ["two\nlines"], ["two\rlines"]]) {
writeFileSync(rulesPath, JSON.stringify({ ...rules, allowed_context_lines: invalid }));
const rejected = runGuard(repo, rulesPath, "--mode", "full");
assert.notEqual(rejected.status, 0);
assert.match(rejected.stderr, /allowed_context_lines/);
}
});
});
test("staged mode reads index bytes rather than the worktree", () => {
requireGuard();
withRepo((repo, rules) => {
write(join(repo, "README.md"), "clean\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "base");
write(join(repo, "README.md"), "operator-agent@kernel\n");
git(repo, "add", "README.md");
write(join(repo, "README.md"), "clean worktree replacement\n");
const result = runGuard(repo, rules, "--mode", "staged");
assert.notEqual(result.status, 0);
assert.match(result.stderr, /operator-agent@/);
assert.doesNotMatch(result.stderr, /clean worktree replacement/);
});
});
test("staged mode handles rename and deletion without reading missing worktree paths", () => {
requireGuard();
withRepo((repo, rules) => {
write(join(repo, "old.md"), "clean\n");
write(join(repo, "delete.md"), "clean\n");
git(repo, "add", ".");
git(repo, "commit", "-m", "base");
git(repo, "mv", "old.md", "renamed.md");
git(repo, "rm", "delete.md");
const result = runGuard(repo, rules, "--mode", "staged");
assert.equal(result.status, 0, result.stderr);
});
});
test("range mode scans exact committed bytes between from and to", () => {
requireGuard();
withRepo((repo, rules) => {
write(join(repo, "README.md"), "clean\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "base");
const from = git(repo, "rev-parse", "HEAD").trim();
write(join(repo, "skills", "public", "SKILL.md"), "mm2-secret\n");
git(repo, "add", ".");
git(repo, "commit", "-m", "leak");
const to = git(repo, "rev-parse", "HEAD").trim();
write(join(repo, "README.md"), "clean uncommitted replacement\n");
const result = runGuard(
repo,
rules,
"--mode",
"range",
"--from",
from,
"--to",
to,
);
assert.notEqual(result.status, 0);
assert.match(result.stderr, /skills\/public\/SKILL\.md/);
assert.match(result.stderr, /mm2-/);
});
});
test("range mode rejects a leak from an intermediate pushed commit even when cleaned at to", () => {
requireGuard();
withRepo((repo, rules) => {
write(join(repo, "README.md"), "clean\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "base");
const from = git(repo, "rev-parse", "HEAD").trim();
write(join(repo, "README.md"), "founder-only intermediate\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "intermediate leak");
write(join(repo, "README.md"), "clean again\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "clean tip");
const to = git(repo, "rev-parse", "HEAD").trim();
const result = runGuard(
repo,
rules,
"--mode",
"range",
"--from",
from,
"--to",
to,
);
assert.notEqual(result.status, 0);
assert.match(result.stderr, /README\.md/);
assert.match(result.stderr, /founder/);
});
});
test("each explicit mode refuses a missing generated rules file", () => {
requireGuard();
withRepo((repo) => {
write(join(repo, "README.md"), "clean\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "base");
const head = git(repo, "rev-parse", "HEAD").trim();
const missing = join(repo, "missing-rules.json");
for (const args of [
["--mode", "full"],
["--mode", "staged"],
["--mode", "range", "--from", head, "--to", head],
["--mode", "tree", "--tree", repo],
]) {
const result = runGuard(repo, missing, ...args);
assert.notEqual(result.status, 0);
assert.match(result.stderr, /missing-rules\.json|rules/i);
}
});
});
test("each explicit mode refuses an empty generated rules object", () => {
requireGuard();
withRepo((repo) => {
write(join(repo, "README.md"), "clean\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "base");
const head = git(repo, "rev-parse", "HEAD").trim();
const empty = join(repo, "empty-rules.json");
writeFileSync(empty, "{}");
for (const args of [
["--mode", "full"],
["--mode", "staged"],
["--mode", "range", "--from", head, "--to", head],
["--mode", "tree", "--tree", repo],
]) {
const result = runGuard(repo, empty, ...args);
assert.notEqual(result.status, 0);
assert.match(result.stderr, /rules|path_prefixes|invalid/i);
}
});
});
test("tree mode scans the actual packaging subtree, including an untracked seeded fixture", () => {
requireGuard();
const root = mkdtempSync(join(tmpdir(), "openrig-guard-tree-"));
const tree = join(root, "specs");
const rules = join(root, "rules.json");
try {
writeFileSync(rules, JSON.stringify(fixtureRules()));
write(join(tree, "agents/private/notes.md"), "operator-agent@kernel\n");
const refused = runGuard(root, rules, "--mode", "tree", "--tree", tree);
assert.notEqual(refused.status, 0);
assert.match(refused.stderr, /agents\/private\/notes\.md/);
assert.match(refused.stderr, /operator-agent@/);
rmSync(join(tree, "agents/private/notes.md"));
write(join(tree, "agents/private/notes.md"), "public fixture\n");
const admitted = runGuard(root, rules, "--mode", "tree", "--tree", tree);
assert.equal(admitted.status, 0, admitted.stderr);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("full artifact mode scans exactly the packager manifest and reports its file set", () => {
requireGuard();
const root = mkdtempSync(join(tmpdir(), "openrig-guard-artifact-"));
const tree = join(root, "package");
const rules = join(root, "rules.json");
const filesManifest = join(root, "artifact-files.json");
const report = join(root, "scan-report.json");
try {
writeFileSync(rules, JSON.stringify(fixtureRules()));
write(join(tree, "daemon/assets/guide.md"), "public guide\n");
write(join(tree, "package.json"), "{}\n");
write(join(tree, "not-packed.md"), "founder-only ignored staging residue\n");
writeFileSync(filesManifest, JSON.stringify({
files: ["package.json", "daemon/assets/guide.md"],
}));
const result = runGuard(
root,
rules,
"--mode", "full",
"--tree", tree,
"--files-manifest", filesManifest,
"--report", report,
);
assert.equal(result.status, 0, result.stderr);
assert.deepEqual(JSON.parse(readFileSync(report, "utf8")), {
mode: "full",
scannedFiles: ["daemon/assets/guide.md", "package.json"],
findingCount: 0,
});
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("Plain B keeps guard enforcement out of root test:repo while fixture tests remain discovered", () => {
const pkg = JSON.parse(readFileSync("package.json", "utf8"));
const command = pkg.scripts["test:repo"];
// Packaging tests rebuild daemon/dist while other script tests import it.
assert.match(command, /node --test --test-concurrency=1 scripts\/\*\.test\.mjs/);
assert.doesNotMatch(command, /node scripts\/check-internal-leak-guard\.mjs/);
});
function withRepo(run) {
const root = mkdtempSync(join(tmpdir(), "openrig-guard-red-"));
const repo = join(root, "repo");
const rules = join(root, "rules.json");
mkdirSync(repo, { recursive: true });
writeFileSync(rules, JSON.stringify(fixtureRules()));
git(repo, "init");
git(repo, "config", "user.email", "[email protected]");
git(repo, "config", "user.name", "QA");
try {
run(repo, rules);
} finally {
rmSync(root, { recursive: true, force: true });
}
}
function requireGuard() {
assert.equal(
existsSync(GUARD),
true,
"scripts/check-internal-leak-guard.mjs must exist",
);
}
function runGuard(repo, rules, ...args) {
return spawnSync(
process.execPath,
[GUARD, "--repo", repo, "--rules", rules, ...args],
{ cwd: repo, encoding: "utf8" },
);
}
function git(repo, ...args) {
return execFileSync("git", args, { cwd: repo, encoding: "utf8" });
}
function write(path, content) {
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, content);
}
function fixtureRules() {
return {
path_prefixes: ["openrig-work/"],
seat_and_rig_patterns: ["operator-agent@"],
host_patterns: ["mm2-"],
charged_terms: ["founder"],
internal_path_globs: ["*.internal.*", "**/internal/**", "*-internal/**"],
allowed_context_substrings: ["do not ship"],
};
}