Files

141 lines
6.4 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import { join } from "node:path";
import {
BusinessError, checkVars, loadBusiness, loadProject, projectFilePath, systemVars,
} from "../src/index.mjs";
import { businessDoc, REPO_ROLES, tmp, writeJson } from "./helpers.mjs";
import { checkRoutes, launchSettings } from "../src/launch-settings.mjs";
import { OrganizationError } from "../src/organization.mjs";
const SECRET = "sk-test-NOT-A-SECRET-1234";
function hidesValue(fn, key, reason) {
assert.throws(fn, (error) => {
assert.ok(error instanceof BusinessError);
assert.equal(error.name, "BusinessError");
assert.equal(error.exitCode, 2);
assert.ok(error.message.includes(`variable ${key}`), error.message);
if (reason) assert.match(error.message, reason);
assert.ok(!error.message.includes(SECRET), "refusal disclosed a var value");
assert.ok(!String(error).includes(SECRET), "rendered error disclosed a var value");
return true;
});
}
for (const [scope, key, value] of [
["business", "tracker.kind", SECRET],
["agent", "thinking", SECRET],
["agent", "model", SECRET],
]) {
test(`organization file ${scope} ${key} refusal names its key and hides its value`, () => {
const root = tmp();
const doc = businessDoc(root);
if (scope === "agent") doc.roles.coder.vars[key] = value;
else doc.vars[key] = value;
writeJson(join(root, "businesses/acme.json"), doc);
hidesValue(() => loadBusiness("acme", { dir: root, rolesDir: REPO_ROLES }), key, key === "model" ? /unknown model/ : /one of/);
});
}
for (const scope of ["project", "role"]) {
test(`project file ${scope} var refusal names its key and hides its value`, () => {
const root = tmp();
const vars = { "tracker.project": SECRET };
writeJson(projectFilePath(root), {
projectVersion: 1, id: "stack",
...(scope === "project" ? { vars } : { roles: { coder: { vars } } }),
});
hidesValue(() => loadProject(root), "tracker.project", /positive integer/);
});
}
test("systemVars accepts user values and refuses malformed ones without quoting them", () => {
const config = {
environment: "development", dataRoot: "/d",
execution: { backend: "docker", provider: "zai", model: "m", adapter: "pi" },
};
for (const key of ["environment", "dataRoot", "execution.backend", "execution.provider", "execution.model", "execution.adapter"]) {
const bad = structuredClone(config);
const value = { pasted: SECRET };
if (key.startsWith("execution.")) bad.execution[key.slice("execution.".length)] = value;
else bad[key] = value;
hidesValue(() => systemVars(bad), key);
}
});
// Exercise every validator that used to quote a scalar, a nested leaf or a duplicate.
// Removing any of their redactions must fail here even if a caller sanitizes its error.
for (const [key, layer, value, reason] of [
["tracker.kind", "business", SECRET, /one of/],
["harness", "agent", `${SECRET}!`, /harness module name/],
["fallback.harness", "agent", `${SECRET}!`, /harness module name/],
["thinking", "agent", { pasted: [SECRET] }, /one of/],
["fallback.thinking", "agent", SECRET, /one of/],
["tracker.reconcileMinutes", "business", SECRET, /integer of at least 1/],
["tracker.pollSeconds", "project", SECRET, /integer of at least 10/],
["tracker.baseUrl", "business", SECRET, /http or https URL/],
["gitea.baseUrl", "business", SECRET, /http or https URL/],
["web.searxng", "business", `http://${SECRET}.example`, /https, or http on loopback/],
["git.workingBranch", "project", `${SECRET}!`, /git branch name/],
["git.protectedBranches", "project", [SECRET, `${SECRET}!`], /git branch name/],
["git.protectedBranches", "project", [SECRET, SECRET], /duplicate entry/],
["suites", "project", [SECRET, SECRET], /duplicate entry/],
["tracker.descendants", "project", SECRET, /true or false/],
["bus.tester", "business", SECRET, /true or false/],
["tracker.project", "project", SECRET, /positive integer/],
["limits.tools", "agent", ["read", SECRET], /unsupported tool/],
["limits.authority", "agent", ["message.send", SECRET], /unknown action/],
["limits.network", "agent", SECRET, /one of/],
]) {
test(`checkVars hides refused ${key}: ${reason.source}`, () => {
hidesValue(() => checkVars({ [key]: value }, layer, "fixture vars"), key, reason);
});
}
for (const prefix of ["", "fallback."]) {
test(`route writer unknown ${prefix}model names its key and hides its value`, () => {
const root = tmp();
const doc = businessDoc(root);
writeJson(join(root, "businesses/acme.json"), doc);
const settings = launchSettings({ dir: root, rolesDir: REPO_ROLES });
const route = { harness: "pi", model: Object.keys(doc.launch.models)[0], thinking: "high", fallback: null };
if (prefix) route.fallback = { harness: "pi", model: SECRET, thinking: "high" };
else route.model = SECRET;
assert.throws(() => settings.setRoutes("acme", {
revision: settings.writer.read("acme").revision, routes: { coder: route },
}, "jason"), error => {
assert.ok(error instanceof OrganizationError);
assert.equal(error.code, "unknown-model");
assert.ok(!String(error).includes(SECRET));
assert.ok(error.message.includes(`variable ${prefix}model`), error.message);
return true;
});
});
for (const [field, code] of [
["model", "unknown-model"], ["harness", "harness-off"],
["harness", "harness-refused"], ["thinking", "thinking-unsupported"],
]) {
test(`route check ${prefix}${field} ${code} names its key and hides its value`, () => {
const vars = prefix ? {
harness: "pi", model: "listed", thinking: "high",
"fallback.harness": "pi", "fallback.model": "listed", "fallback.thinking": "high",
} : { harness: "pi", model: "listed", thinking: "high" };
const key = `${prefix}${field}`;
vars[key] = SECRET;
const proposed = {
roles: { coder: { vars } }, launch: { models: { listed: {} } },
plugins: { harness: code === "harness-refused" ? ["pi", SECRET] : ["pi"] },
};
assert.throws(() => checkRoutes({ proposed, levels: name => name === SECRET ? null : ["high"] }), error => {
assert.ok(error instanceof OrganizationError);
assert.equal(error.code, code);
assert.ok(!String(error).includes(SECRET), "route refusal disclosed a var value");
assert.ok(error.message.includes(`variable ${key}`), error.message);
return true;
});
});
}
}