// Row 298 (#1804, Q228): the chief of staff's grant scope, launch.grantScope. The shape check, the // readers' fallback, the match rules (sage-3594 Q2) and the one writer. The broker's verbs, the // in-scope close and the executors are bus tests (packages/bus/tests/grant-scope.test.mjs). import test, { after } from "node:test"; import assert from "node:assert/strict"; import { readFileSync, rmSync } from "node:fs"; import { join } from "node:path"; import { instanceSettings } from "../src/instance-settings.mjs"; import { launchSettings } from "../src/launch-settings.mjs"; import { loadBusiness } from "../src/organization-config.mjs"; import { checkGrantScope, grantInScope, grantScopeOf } from "../src/vocabulary.mjs"; import { businessDoc, rolesCopy, systemFor, tmp, writeJson } from "./helpers.mjs"; const roots = []; after(() => roots.forEach(root => rmSync(root, { recursive: true, force: true }))); const GITEA = { gitea: { agents: ["coder"], projects: ["app", "lib"] } }; const HOST = { host: { agents: ["ops"], hosts: ["web1"], commands: [["systemctl", "status", "nginx"], ["ls"]] } }; const gitea = (over = {}) => ({ method: "gitea", agent: "coder", target: { projects: ["app"] }, ...over }); const host = (commands, over = {}) => ({ method: "host", agent: "ops", host: "web1", commands, ...over }); function fixture(mutate) { const root = tmp(); roots.push(root); const rolesDir = rolesCopy(root); const doc = businessDoc(root, "acme"); mutate?.(doc); const dir = join(root, "config"); const file = writeJson(join(dir, "businesses", "acme.json"), doc); const settings = instanceSettings({ dir, rolesDir, system: systemFor(root) }); const limits = launchSettings({ dir, rolesDir, system: systemFor(root) }); const onDisk = () => JSON.parse(readFileSync(file, "utf8")); const log = () => readFileSync(settings.writer.logFile("acme"), "utf8").trim().split("\n").map(line => JSON.parse(line)); const revision = () => settings.read("acme").revision; return { root, dir, rolesDir, file, settings, limits, onDisk, log, revision }; } test("row 298: checkGrantScope takes the D2 shape and refuses anything else", () => { assert.deepEqual(checkGrantScope({}), {}); assert.deepEqual(checkGrantScope(GITEA), GITEA); assert.deepEqual(checkGrantScope(HOST), HOST); assert.deepEqual(checkGrantScope({ host: { agents: ["ops"], hosts: ["web1"], commands: [["uptime"]] } }).host.commands, [["uptime"]]); for (const bad of [ null, [], "x", { other: {} }, { gitea: [] }, { gitea: { agents: ["coder"] } }, { gitea: { agents: [], projects: ["app"] } }, { gitea: { agents: ["coder", "coder"], projects: ["app"] } }, { gitea: { agents: ["Coder"], projects: ["app"] } }, { gitea: { agents: ["coder"], projects: ["app"], extra: 1 } }, { host: { agents: ["ops"], hosts: ["web1"] } }, { host: { agents: ["ops"], hosts: ["web_1"], commands: [["ls"]] } }, { host: { agents: ["ops"], hosts: ["web1"], commands: [[]] } }, { host: { agents: ["ops"], hosts: ["web1"], commands: [["ls"], ["ls"]] } }, { host: { agents: ["ops"], hosts: ["web1"], commands: [["bin/ls"]] } }, { host: { agents: ["ops"], hosts: ["web1"], commandPrefixes: [["ls"]] } }, { "host-enroll": {} }, ]) assert.equal(checkGrantScope(bad), null, JSON.stringify(bad)); }); test("row 298: a missing scope is empty and a bad one falls back to empty, which covers nothing", () => { assert.deepEqual(grantScopeOf(undefined), { scope: {}, fallback: false }); assert.deepEqual(grantScopeOf(GITEA), { scope: GITEA, fallback: false }); const bad = { gitea: { agents: ["coder"], projects: ["app"], extra: true } }; assert.deepEqual(grantScopeOf(bad), { scope: {}, fallback: true }); assert.equal(grantInScope(grantScopeOf(bad).scope, gitea()), false); assert.equal(grantInScope({}, gitea()), false); assert.equal(grantInScope({}, host([["ls"]])), false); }); test("row 298: a Gitea grant is in scope only when its agent and every project are listed", () => { assert.equal(grantInScope(GITEA, gitea()), true); assert.equal(grantInScope(GITEA, gitea({ target: { projects: ["app", "lib"] } })), true); assert.equal(grantInScope(GITEA, gitea({ target: { projects: ["app", "old"] } })), false); assert.equal(grantInScope(GITEA, gitea({ agent: "research" })), false); assert.equal(grantInScope(GITEA, gitea({ target: { projects: [] } })), false); assert.equal(grantInScope(HOST, gitea()), false); }); test("row 298 (sage-3594 Q2): host commands match whole argv tokens, and enrollment is never in scope", () => { assert.equal(grantInScope(HOST, host([["systemctl", "status", "nginx"]])), true); // A commands entry covers only itself: no extra trailing arguments, no shorter prefix. assert.equal(grantInScope(HOST, host([["systemctl", "status", "nginx", "--now"]])), false); assert.equal(grantInScope(HOST, host([["systemctl", "status"]])), false); // No scope entry permits extending the requested command prefix. assert.equal(grantInScope(HOST, host([["ls"]])), true); assert.equal(grantInScope(HOST, host([["ls", "-l", "/var"]])), false); assert.equal(grantInScope(HOST, host([["lsblk"]])), false); assert.equal(grantInScope(HOST, host([["ls"], ["lsblk"]])), false); assert.equal(grantInScope(HOST, host([["ls"]], { host: "web2" })), false); assert.equal(grantInScope(HOST, host([["ls"]], { agent: "coder" })), false); assert.equal(grantInScope(HOST, { method: "host-enroll", host: "web1" }), false); assert.equal(grantInScope({ ...HOST, "host-enroll": true }, { method: "host-enroll", host: "web1" }), false); }); test("row 298: a bad grantScope in the file still loads, and the read shows the fallback", () => { const f = fixture(doc => { doc.launch.grantScope = { gitea: "everything" }; }); const business = loadBusiness("acme", { dir: f.dir, rolesDir: f.rolesDir }); assert.deepEqual(business.launch.grantScope, { gitea: "everything" }); assert.deepEqual(f.settings.grantScopeRead("acme"), { business: "acme", revision: f.revision(), scope: {}, fallback: true }); }); test("row 298: the writer checks the scope, logs the change and removes the key for {}", () => { const f = fixture(); assert.deepEqual(f.settings.grantScopeRead("acme").scope, {}); const r = f.settings.grantScope("acme", { revision: f.revision(), scope: GITEA }, "jason"); assert.deepEqual(r.scope, GITEA); assert.deepEqual(r.old, {}); assert.ok(typeof r.transaction === "string" && r.transaction.length > 0); assert.deepEqual(f.onDisk().launch.grantScope, GITEA); assert.equal(f.log().at(-1).actor, "jason"); const back = f.settings.grantScope("acme", { revision: f.revision(), scope: {} }, "jason"); assert.deepEqual(back.old, GITEA); assert.equal(Object.hasOwn(f.onDisk().launch, "grantScope"), false); const bytes = readFileSync(f.file, "utf8"); assert.throws(() => f.settings.grantScope("acme", { revision: f.revision(), scope: { gitea: {} } }, "jason"), e => e.code === "invalid-request"); assert.throws(() => f.settings.grantScope("acme", { revision: f.revision(), scope: GITEA, extra: 1 }, "jason"), e => e.code === "invalid-request"); assert.throws(() => f.settings.grantScope("acme", { revision: "0".repeat(64), scope: GITEA }, "jason"), e => e.code === "revision-conflict"); assert.throws(() => f.settings.grantScope("acme", { revision: f.revision(), scope: {} }, "jason"), e => e.code === "organization-no-change"); assert.equal(readFileSync(f.file, "utf8"), bytes); }); test("row 298 (sage-3594 Q1): the general writers can't write grantScope", () => { const f = fixture(); const bytes = readFileSync(f.file, "utf8"); assert.throws(() => f.limits.set("acme", { revision: f.revision(), grantScope: GITEA }, "jason")); assert.throws(() => f.limits.set("acme", { revision: f.revision(), grantScope: GITEA }, "chief-of-staff.chief")); assert.throws(() => f.settings.set("acme", { revision: f.revision(), instance: "coder", op: "change", grantScope: GITEA }, "jason"), e => e.code === "invalid-request"); assert.throws(() => f.settings.veto("acme", { revision: f.revision(), op: "keep", name: "coder", grantScope: GITEA }, "jason"), e => e.code === "invalid-request"); assert.equal(readFileSync(f.file, "utf8"), bytes); });