import test from "node:test"; import assert from "node:assert/strict"; import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync, rmSync } from "node:fs"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { runRemediation, type RemediationDeps } from "../bin/remediate-run.js"; import type { StorageResource } from "../registry.js"; function fixture(): { root: string; accountsDir: string; configPath: string } { const root = mkdtempSync(join(tmpdir(), "sb-remediate-")); const accountsDir = join(root, "data", "accounts"); for (const acc of ["acc-a", "acc-b"]) { mkdirSync(join(accountsDir, acc, "secrets"), { recursive: true }); writeFileSync( join(accountsDir, acc, "secrets", "cloudflare.env"), `CLOUDFLARE_ACCOUNT_ID=shared\nCLOUDFLARE_API_TOKEN=cfat_master\nCF_PAGES_D1_TOKEN=cfat_pd\nCF_DNS_TOKEN=keep\n`, ); } return { root, accountsDir, configPath: join(root, "config", "cloudflare-house.env") }; } function baseDeps(f: ReturnType, registered: StorageResource[]): RemediationDeps { return { cfD1: async () => [{ name: "gls-leads", cfResourceId: "u1" }], cfR2: async () => [], mapping: { "gls-leads": "acc-a" }, register: async (r) => { registered.push(r); }, accountsDir: f.accountsDir, master: { apiToken: "cfat_master", accountId: "shared" }, configPath: f.configPath, }; } test("fails closed on an unmapped resource and writes nothing", async () => { const f = fixture(); const registered: StorageResource[] = []; const deps = { ...baseDeps(f, registered), cfD1: async () => [ { name: "gls-leads", cfResourceId: "u1" }, { name: "beagle-investors", cfResourceId: "u2" }, ] }; try { await assert.rejects(() => runRemediation(deps), /unmapped resources: beagle-investors/); assert.equal(registered.length, 0, "no resource registered on abort"); assert.equal(existsSync(f.configPath), false, "no house config written on abort"); const secrets = readFileSync(join(f.accountsDir, "acc-a", "secrets", "cloudflare.env"), "utf8"); assert.match(secrets, /CLOUDFLARE_API_TOKEN=cfat_master/, "secrets untouched on abort"); } finally { rmSync(f.root, { recursive: true, force: true }); } }); test("registers, relocates the master, and strips every account-wide token", async () => { const f = fixture(); const registered: StorageResource[] = []; try { const result = await runRemediation(baseDeps(f, registered)); assert.equal(result.registered, 1); assert.deepEqual(registered, [ { accountId: "acc-a", kind: "d1", name: "gls-leads", cfResourceId: "u1" }, ]); // House config now holds the master. const house = readFileSync(f.configPath, "utf8"); assert.match(house, /CLOUDFLARE_API_TOKEN=cfat_master/); assert.match(house, /CLOUDFLARE_ACCOUNT_ID=shared/); // Both sub-account secrets are stripped of account-wide tokens, scoped kept. for (const acc of ["acc-a", "acc-b"]) { const s = readFileSync(join(f.accountsDir, acc, "secrets", "cloudflare.env"), "utf8"); assert.doesNotMatch(s, /CLOUDFLARE_API_TOKEN=/); assert.doesNotMatch(s, /CF_PAGES_D1_TOKEN=/); assert.match(s, /CF_DNS_TOKEN=keep/); assert.match(s, /CLOUDFLARE_ACCOUNT_ID=shared/); } assert.equal(result.strippedFiles.length, 2); } finally { rmSync(f.root, { recursive: true, force: true }); } });