From 5d853c6fced862f2a41e2cc65bfabe9be766bf1e Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 2 Aug 2026 20:04:24 -0700 Subject: [PATCH] test(policy): centralize doctor fixtures (#118392) --- .../src/doctor/register.base.test-utils.ts | 26 +- ...r.gateway-data-and-approvals.test-utils.ts | 21 +- ...register.ingress-and-secrets.test-utils.ts | 16 +- .../register.models-and-mcp.test-utils.ts | 21 +- .../register.sandbox-and-tools.test-utils.ts | 944 +++++++----------- .../src/doctor/register.test-harness.ts | 25 +- 6 files changed, 387 insertions(+), 666 deletions(-) diff --git a/extensions/policy/src/doctor/register.base.test-utils.ts b/extensions/policy/src/doctor/register.base.test-utils.ts index 3a0fadecf959..4b4465fcfd40 100644 --- a/extensions/policy/src/doctor/register.base.test-utils.ts +++ b/extensions/policy/src/doctor/register.base.test-utils.ts @@ -20,32 +20,18 @@ import { ctx, repairCtx, runPolicyChecks, + runPolicyChecksFixture, runDeniedChannelRepair, runPolicyRepairCheck, - describe0BeforeEach0, - describe0AfterEach1, + setupPolicyDoctorTest, + teardownPolicyDoctorTest, + writePolicyFixture, } from "./register.test-harness.js"; -async function writePolicyFixture(...json: Parameters): Promise { - const [policy] = json; - const configPath = join(workspaceDir, "openclaw.jsonc"); - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - typeof policy === "string" ? policy : JSON.stringify(...json), - "utf-8", - ); - return configPath; -} - -async function runPolicyChecksFixture(policy: unknown) { - return runPolicyChecks(ctx(await writePolicyFixture(policy), cfgWithPolicy())); -} - describe("registerPolicyDoctorChecks", () => { - beforeEach(describe0BeforeEach0); + beforeEach(setupPolicyDoctorTest); - afterEach(describe0AfterEach1); + afterEach(teardownPolicyDoctorTest); it("allows scoped overrides that are stricter than top-level policy", async () => { const result = await runPolicyChecksFixture({ diff --git a/extensions/policy/src/doctor/register.gateway-data-and-approvals.test-utils.ts b/extensions/policy/src/doctor/register.gateway-data-and-approvals.test-utils.ts index cfc86918043a..f9f648c0308d 100644 --- a/extensions/policy/src/doctor/register.gateway-data-and-approvals.test-utils.ts +++ b/extensions/policy/src/doctor/register.gateway-data-and-approvals.test-utils.ts @@ -11,22 +11,11 @@ import { ctx, registerChecks, runPolicyDoctorLint, - describe0BeforeEach0, - describe0AfterEach1, + setupPolicyDoctorTest, + teardownPolicyDoctorTest, + writePolicyFixture, } from "./register.test-harness.js"; -async function writePolicyFixture(...json: Parameters): Promise { - const [policy] = json; - const configPath = join(workspaceDir, "openclaw.jsonc"); - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - typeof policy === "string" ? policy : JSON.stringify(...json), - "utf-8", - ); - return configPath; -} - function writeExecApprovalsPolicyFixture(execApprovals: object): Promise { return writePolicyFixture({ execApprovals }); } @@ -41,9 +30,9 @@ function runRegisteredPolicyDoctor(configPath: string, cfg: OpenClawConfig) { } describe("registerPolicyDoctorChecks", () => { - beforeEach(describe0BeforeEach0); + beforeEach(setupPolicyDoctorTest); - afterEach(describe0AfterEach1); + afterEach(teardownPolicyDoctorTest); it("does not report Responses URL fetching when it is disabled", async () => { const cfg = { diff --git a/extensions/policy/src/doctor/register.ingress-and-secrets.test-utils.ts b/extensions/policy/src/doctor/register.ingress-and-secrets.test-utils.ts index 7114f66e64e2..91faaa4dd627 100644 --- a/extensions/policy/src/doctor/register.ingress-and-secrets.test-utils.ts +++ b/extensions/policy/src/doctor/register.ingress-and-secrets.test-utils.ts @@ -1,18 +1,16 @@ // Imported by register.test.ts to keep its mocked suite in one Vitest module graph. -import { promises as fs } from "node:fs"; -import { join } from "node:path"; import { runDoctorLintChecks, type OpenClawConfig } from "openclaw/plugin-sdk/health"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { collectPolicyEvidence } from "../policy-state.js"; import { registerPolicyDoctorChecks } from "./register.js"; import { - workspaceDir, cfgWithPolicy, ctx, runPolicyChecks, runPolicyDoctorLint, - describe0BeforeEach0, - describe0AfterEach1, + setupPolicyDoctorTest, + teardownPolicyDoctorTest, + writePolicyFixture, } from "./register.test-harness.js"; const scanPolicyIngress = (cfg: object) => @@ -32,9 +30,7 @@ const INGRESS_POLICY = { }; async function runPolicyScenario(cfg: OpenClawConfig, policy: object, mode: PolicyScenarioMode) { - const configPath = join(workspaceDir, "openclaw.jsonc"); - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile(join(workspaceDir, "policy.jsonc"), JSON.stringify(policy), "utf-8"); + const configPath = await writePolicyFixture(policy); const checkContext = ctx(configPath, cfg); if (mode === "doctor") { return runPolicyDoctorLint(checkContext); @@ -99,9 +95,9 @@ function policyAgentScope(agentIds: string[], allowedAccess?: string[], allowHos } describe("registerPolicyDoctorChecks", () => { - beforeEach(describe0BeforeEach0); + beforeEach(setupPolicyDoctorTest); - afterEach(describe0AfterEach1); + afterEach(teardownPolicyDoctorTest); it("ignores nested groupPolicy when channel ingress is disabled", async () => { const { result } = await runIngressPolicyScenario({ diff --git a/extensions/policy/src/doctor/register.models-and-mcp.test-utils.ts b/extensions/policy/src/doctor/register.models-and-mcp.test-utils.ts index 3120df735231..0726c8603896 100644 --- a/extensions/policy/src/doctor/register.models-and-mcp.test-utils.ts +++ b/extensions/policy/src/doctor/register.models-and-mcp.test-utils.ts @@ -13,8 +13,9 @@ import { runPolicyChecks, runPolicyDoctorLint, runPolicyRepairCheck, - describe0BeforeEach0, - describe0AfterEach1, + setupPolicyDoctorTest, + teardownPolicyDoctorTest, + writePolicyFixture, } from "./register.test-harness.js"; const scanPolicyMcpServers = (cfg: object) => @@ -22,18 +23,6 @@ const scanPolicyMcpServers = (cfg: object) => const scanPolicyIngress = (cfg: object) => collectPolicyEvidence(cfg as Record).ingress ?? []; -async function writePolicyFixture(...json: Parameters): Promise { - const [policy] = json; - const configPath = join(workspaceDir, "openclaw.jsonc"); - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - typeof policy === "string" ? policy : JSON.stringify(...json), - "utf-8", - ); - return configPath; -} - function writeModelPolicyFixture(providers: object): Promise { return writePolicyFixture({ models: { providers } }); } @@ -51,9 +40,9 @@ function writeIngressPolicyFixture(ingress: object): Promise { } describe("registerPolicyDoctorChecks", () => { - beforeEach(describe0BeforeEach0); + beforeEach(setupPolicyDoctorTest); - afterEach(describe0AfterEach1); + afterEach(teardownPolicyDoctorTest); it("repairs required agent workspace deny tool findings", async () => { const cfg = { diff --git a/extensions/policy/src/doctor/register.sandbox-and-tools.test-utils.ts b/extensions/policy/src/doctor/register.sandbox-and-tools.test-utils.ts index af85f00707ab..4a48cecb8f14 100644 --- a/extensions/policy/src/doctor/register.sandbox-and-tools.test-utils.ts +++ b/extensions/policy/src/doctor/register.sandbox-and-tools.test-utils.ts @@ -1,23 +1,21 @@ // Imported by register.test.ts to keep its mocked suite in one Vitest module graph. -import { promises as fs } from "node:fs"; -import { join } from "node:path"; import { runDoctorLintChecks, type OpenClawConfig } from "openclaw/plugin-sdk/health"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { collectPolicyEvidence } from "../policy-state.js"; import { registerPolicyDoctorChecks } from "./register.js"; import { - workspaceDir, cfgWithPolicy, ctx, runPolicyChecks, - describe0BeforeEach0, - describe0AfterEach1, + setupPolicyDoctorTest, + teardownPolicyDoctorTest, + writePolicyFixture, } from "./register.test-harness.js"; describe("registerPolicyDoctorChecks", () => { - beforeEach(describe0BeforeEach0); + beforeEach(setupPolicyDoctorTest); - afterEach(describe0AfterEach1); + afterEach(teardownPolicyDoctorTest); it("ignores agent-local Docker and browser posture under shared sandbox scope", async () => { const cfg = { @@ -98,7 +96,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("treats blank agent browser CDP source range as an explicit clear", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), agents: { @@ -119,16 +116,11 @@ describe("registerPolicyDoctorChecks", () => { ], }, } as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - sandbox: { - browser: { requireCdpSourceRange: true }, - }, - }), - "utf-8", - ); + const configPath = await writePolicyFixture({ + sandbox: { + browser: { requireCdpSourceRange: true }, + }, + }); const result = await runPolicyChecks(ctx(configPath, cfg)); @@ -143,7 +135,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("reports enabled container posture rules that the backend cannot observe", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), agents: { @@ -160,21 +151,16 @@ describe("registerPolicyDoctorChecks", () => { }, }, } as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - sandbox: { - allowBackends: ["openshell"], - containers: { - denyHostNetwork: true, - denyContainerRuntimeSocketMounts: true, - denyUnconfinedProfiles: true, - }, + const configPath = await writePolicyFixture({ + sandbox: { + allowBackends: ["openshell"], + containers: { + denyHostNetwork: true, + denyContainerRuntimeSocketMounts: true, + denyUnconfinedProfiles: true, }, - }), - "utf-8", - ); + }, + }); const result = await runPolicyChecks(ctx(configPath, cfg)); const evidence = collectPolicyEvidence(cfg as unknown as Record); @@ -223,7 +209,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("evaluates inherited container mounts for browser containers on non-Docker backends", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), agents: { @@ -242,20 +227,15 @@ describe("registerPolicyDoctorChecks", () => { }, }, } as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - sandbox: { - allowBackends: ["openshell"], - containers: { - requireReadOnlyMounts: true, - denyContainerRuntimeSocketMounts: true, - }, + const configPath = await writePolicyFixture({ + sandbox: { + allowBackends: ["openshell"], + containers: { + requireReadOnlyMounts: true, + denyContainerRuntimeSocketMounts: true, }, - }), - "utf-8", - ); + }, + }); const result = await runPolicyChecks(ctx(configPath, cfg)); const evidence = collectPolicyEvidence(cfg as unknown as Record); @@ -285,7 +265,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("normalizes mixed-case Docker backend before collecting container posture", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), agents: { @@ -302,21 +281,16 @@ describe("registerPolicyDoctorChecks", () => { }, }, } as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - sandbox: { - allowBackends: ["docker"], - containers: { - denyHostNetwork: true, - denyContainerRuntimeSocketMounts: true, - denyUnconfinedProfiles: true, - }, + const configPath = await writePolicyFixture({ + sandbox: { + allowBackends: ["docker"], + containers: { + denyHostNetwork: true, + denyContainerRuntimeSocketMounts: true, + denyUnconfinedProfiles: true, }, - }), - "utf-8", - ); + }, + }); const result = await runPolicyChecks(ctx(configPath, cfg)); const evidence = collectPolicyEvidence(cfg as unknown as Record); @@ -343,7 +317,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("evaluates Podman container posture without reporting it as unobservable", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), agents: { @@ -360,21 +333,16 @@ describe("registerPolicyDoctorChecks", () => { }, }, } as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - sandbox: { - allowBackends: ["podman"], - containers: { - denyHostNetwork: true, - denyContainerRuntimeSocketMounts: true, - denyUnconfinedProfiles: true, - }, + const configPath = await writePolicyFixture({ + sandbox: { + allowBackends: ["podman"], + containers: { + denyHostNetwork: true, + denyContainerRuntimeSocketMounts: true, + denyUnconfinedProfiles: true, }, - }), - "utf-8", - ); + }, + }); const result = await runPolicyChecks(ctx(configPath, cfg)); @@ -454,7 +422,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("accepts configured sandbox posture that matches policy", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), agents: { @@ -472,25 +439,20 @@ describe("registerPolicyDoctorChecks", () => { }, }, } as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - sandbox: { - requireMode: ["all", "non-main"], - allowBackends: ["docker"], - containers: { - denyHostNetwork: true, - denyContainerNamespaceJoin: true, - requireReadOnlyMounts: true, - denyContainerRuntimeSocketMounts: true, - denyUnconfinedProfiles: true, - }, - browser: { requireCdpSourceRange: true }, + const configPath = await writePolicyFixture({ + sandbox: { + requireMode: ["all", "non-main"], + allowBackends: ["docker"], + containers: { + denyHostNetwork: true, + denyContainerNamespaceJoin: true, + requireReadOnlyMounts: true, + denyContainerRuntimeSocketMounts: true, + denyUnconfinedProfiles: true, }, - }), - "utf-8", - ); + browser: { requireCdpSourceRange: true }, + }, + }); const result = await runPolicyChecks(ctx(configPath, cfg)); @@ -498,7 +460,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("applies agent-scoped sandbox claims only to matching agents", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), agents: { @@ -508,24 +469,19 @@ describe("registerPolicyDoctorChecks", () => { ], }, } as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - sandbox: { - requireMode: ["all"], - }, - scopes: { - sebby: { - agentIds: ["sebby"], - sandbox: { - allowBackends: ["docker"], - }, + const configPath = await writePolicyFixture({ + sandbox: { + requireMode: ["all"], + }, + scopes: { + sebby: { + agentIds: ["sebby"], + sandbox: { + allowBackends: ["docker"], }, }, - }), - "utf-8", - ); + }, + }); const result = await runPolicyChecks(ctx(configPath, cfg)); @@ -554,29 +510,23 @@ describe("registerPolicyDoctorChecks", () => { }); it("does not apply sandbox overlays from invalid scoped policy", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), agents: { list: [{ id: "sebby", sandbox: { mode: "off" } }], }, } as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - scopes: { - sebby: { - agentIds: ["sebby"], - channels: { allow: ["discord"] }, - sandbox: { - requireMode: ["all"], - }, + const configPath = await writePolicyFixture({ + scopes: { + sebby: { + agentIds: ["sebby"], + channels: { allow: ["discord"] }, + sandbox: { + requireMode: ["all"], }, }, - }), - "utf-8", - ); + }, + }); const result = await runPolicyChecks(ctx(configPath, cfg)); @@ -599,7 +549,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("reports scoped container posture rules that a non-Docker agent group cannot observe", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), agents: { @@ -621,21 +570,16 @@ describe("registerPolicyDoctorChecks", () => { ], }, } as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - scopes: { - release: { - agentIds: ["release-agent"], - sandbox: { - containers: { requireReadOnlyMounts: true }, - }, + const configPath = await writePolicyFixture({ + scopes: { + release: { + agentIds: ["release-agent"], + sandbox: { + containers: { requireReadOnlyMounts: true }, }, }, - }), - "utf-8", - ); + }, + }); const result = await runPolicyChecks(ctx(configPath, cfg)); @@ -649,7 +593,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("allows scoped non-Docker agent groups when container posture rules are off", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), agents: { @@ -671,21 +614,16 @@ describe("registerPolicyDoctorChecks", () => { ], }, } as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - scopes: { - release: { - agentIds: ["release-agent"], - sandbox: { - containers: { requireReadOnlyMounts: false }, - }, + const configPath = await writePolicyFixture({ + scopes: { + release: { + agentIds: ["release-agent"], + sandbox: { + containers: { requireReadOnlyMounts: false }, }, }, - }), - "utf-8", - ); + }, + }); const result = await runPolicyChecks(ctx(configPath, cfg)); @@ -693,7 +631,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("does not fall back to default browser posture for scoped browser-disabled agents", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), agents: { @@ -712,22 +649,17 @@ describe("registerPolicyDoctorChecks", () => { ], }, } as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - scopes: { - release: { - agentIds: ["release-agent"], - sandbox: { - containers: { denyHostNetwork: true }, - browser: { requireCdpSourceRange: true }, - }, + const configPath = await writePolicyFixture({ + scopes: { + release: { + agentIds: ["release-agent"], + sandbox: { + containers: { denyHostNetwork: true }, + browser: { requireCdpSourceRange: true }, }, }, - }), - "utf-8", - ); + }, + }); const result = await runPolicyChecks(ctx(configPath, cfg)); const evidence = collectPolicyEvidence(cfg as unknown as Record); @@ -750,7 +682,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("applies main-scoped sandbox claims to defaults when unrelated agents exist", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), agents: { @@ -765,19 +696,14 @@ describe("registerPolicyDoctorChecks", () => { ], }, } as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - scopes: { - mainSandbox: { - agentIds: ["main"], - sandbox: { requireMode: ["all"] }, - }, + const configPath = await writePolicyFixture({ + scopes: { + mainSandbox: { + agentIds: ["main"], + sandbox: { requireMode: ["all"] }, }, - }), - "utf-8", - ); + }, + }); const result = await runPolicyChecks(ctx(configPath, cfg)); @@ -793,7 +719,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("reports tool posture denied by policy", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), tools: { @@ -818,24 +743,19 @@ describe("registerPolicyDoctorChecks", () => { ], }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - tools: { - profiles: { allow: ["messaging", "minimal"] }, - fs: { requireWorkspaceOnly: true }, - exec: { - allowSecurity: ["deny", "allowlist"], - requireAsk: ["always"], - allowHosts: ["sandbox"], - }, - elevated: { allow: false }, - denyTools: ["exec", "write", "edit", "apply_patch"], + const configPath = await writePolicyFixture({ + tools: { + profiles: { allow: ["messaging", "minimal"] }, + fs: { requireWorkspaceOnly: true }, + exec: { + allowSecurity: ["deny", "allowlist"], + requireAsk: ["always"], + allowHosts: ["sandbox"], }, - }), - "utf-8", - ); + elevated: { allow: false }, + denyTools: ["exec", "write", "edit", "apply_patch"], + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -914,7 +834,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("accepts configured tool posture that matches policy", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), tools: { @@ -925,24 +844,19 @@ describe("registerPolicyDoctorChecks", () => { elevated: { enabled: false }, }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - tools: { - profiles: { allow: ["messaging", "minimal"] }, - fs: { requireWorkspaceOnly: true }, - exec: { - allowSecurity: ["deny"], - requireAsk: ["always"], - allowHosts: ["sandbox"], - }, - elevated: { allow: false }, - denyTools: ["exec", "write", "edit", "apply_patch"], + const configPath = await writePolicyFixture({ + tools: { + profiles: { allow: ["messaging", "minimal"] }, + fs: { requireWorkspaceOnly: true }, + exec: { + allowSecurity: ["deny"], + requireAsk: ["always"], + allowHosts: ["sandbox"], }, - }), - "utf-8", - ); + elevated: { allow: false }, + denyTools: ["exec", "write", "edit", "apply_patch"], + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -951,7 +865,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("reports global and agent-scoped tool claims independently", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), tools: { @@ -964,24 +877,19 @@ describe("registerPolicyDoctorChecks", () => { ], }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - tools: { - exec: { allowHosts: ["sandbox", "gateway"] }, - }, - scopes: { - sebby: { - agentIds: ["sebby"], - tools: { - exec: { allowHosts: ["gateway"] }, - }, + const configPath = await writePolicyFixture({ + tools: { + exec: { allowHosts: ["sandbox", "gateway"] }, + }, + scopes: { + sebby: { + agentIds: ["sebby"], + tools: { + exec: { allowHosts: ["gateway"] }, }, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1010,7 +918,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("does not apply agent-scoped tool claims to other agents", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), agents: { @@ -1020,21 +927,16 @@ describe("registerPolicyDoctorChecks", () => { ], }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - scopes: { - sebby: { - agentIds: ["sebby"], - tools: { - exec: { allowHosts: ["sandbox"] }, - }, + const configPath = await writePolicyFixture({ + scopes: { + sebby: { + agentIds: ["sebby"], + tools: { + exec: { allowHosts: ["sandbox"] }, }, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1043,7 +945,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("reports global and agent-scoped alsoAllow drift", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), tools: { alsoAllow: ["read", "cron"] }, @@ -1054,24 +955,19 @@ describe("registerPolicyDoctorChecks", () => { ], }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - tools: { - alsoAllow: { expected: ["read", "message"] }, - }, - scopes: { - sebby: { - agentIds: ["sebby"], - tools: { - alsoAllow: { expected: ["read", "message"] }, - }, + const configPath = await writePolicyFixture({ + tools: { + alsoAllow: { expected: ["read", "message"] }, + }, + scopes: { + sebby: { + agentIds: ["sebby"], + tools: { + alsoAllow: { expected: ["read", "message"] }, }, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1111,21 +1007,15 @@ describe("registerPolicyDoctorChecks", () => { }); it("reports unexpected alsoAllow entries when policy expects none", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), tools: { alsoAllow: ["read"] }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - tools: { - alsoAllow: { expected: [] }, - }, - }), - "utf-8", - ); + const configPath = await writePolicyFixture({ + tools: { + alsoAllow: { expected: [] }, + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1140,28 +1030,22 @@ describe("registerPolicyDoctorChecks", () => { }); it("uses config-level exec defaults and normalizes required deny aliases", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), tools: { deny: ["exec", "apply_patch"], }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - tools: { - exec: { - allowSecurity: ["deny"], - requireAsk: ["always"], - allowHosts: ["auto"], - }, - denyTools: ["bash", "apply-patch"], + const configPath = await writePolicyFixture({ + tools: { + exec: { + allowSecurity: ["deny"], + requireAsk: ["always"], + allowHosts: ["auto"], }, - }), - "utf-8", - ); + denyTools: ["bash", "apply-patch"], + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1188,28 +1072,22 @@ describe("registerPolicyDoctorChecks", () => { }); it("accepts omitted exec defaults and individual denies for required deny groups", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), tools: { deny: ["exec", "process", "code_execution", "read", "write", "edit", "apply_patch"], }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - tools: { - exec: { - allowSecurity: ["full"], - requireAsk: ["off"], - allowHosts: ["auto"], - }, - denyTools: ["group:runtime", "group:fs"], + const configPath = await writePolicyFixture({ + tools: { + exec: { + allowSecurity: ["full"], + requireAsk: ["off"], + allowHosts: ["auto"], }, - }), - "utf-8", - ); + denyTools: ["group:runtime", "group:fs"], + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1218,23 +1096,17 @@ describe("registerPolicyDoctorChecks", () => { }); it("accepts wildcard tool denies for required tool posture", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), tools: { deny: ["web_*"], }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - tools: { - denyTools: ["web_search"], - }, - }), - "utf-8", - ); + const configPath = await writePolicyFixture({ + tools: { + denyTools: ["web_search"], + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1243,23 +1115,17 @@ describe("registerPolicyDoctorChecks", () => { }); it("accepts canonical tool groups for required tool denies", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), tools: { deny: ["group:openclaw"], }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - tools: { - denyTools: ["message"], - }, - }), - "utf-8", - ); + const configPath = await writePolicyFixture({ + tools: { + denyTools: ["message"], + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1279,7 +1145,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("treats globally disabled elevated mode as disabling per-agent elevated posture", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), tools: { @@ -1296,16 +1161,11 @@ describe("registerPolicyDoctorChecks", () => { ], }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - tools: { - elevated: { allow: false }, - }, - }), - "utf-8", - ); + const configPath = await writePolicyFixture({ + tools: { + elevated: { allow: false }, + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1325,18 +1185,12 @@ describe("registerPolicyDoctorChecks", () => { }); it("treats omitted tool profile as full posture for profile allow policy", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = cfgWithPolicy(); - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - tools: { - profiles: { allow: ["messaging"] }, - }, - }), - "utf-8", - ); + const configPath = await writePolicyFixture({ + tools: { + profiles: { allow: ["messaging"] }, + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1351,26 +1205,20 @@ describe("registerPolicyDoctorChecks", () => { }); it("uses deny as the omitted exec security default for explicit sandbox host", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), tools: { exec: { host: "sandbox" }, }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - tools: { - exec: { - allowSecurity: ["deny"], - allowHosts: ["sandbox"], - }, + const configPath = await writePolicyFixture({ + tools: { + exec: { + allowSecurity: ["deny"], + allowHosts: ["sandbox"], }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1390,7 +1238,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("uses deny as the omitted exec security default for auto host when sandbox can apply", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), agents: { @@ -1399,19 +1246,14 @@ describe("registerPolicyDoctorChecks", () => { }, }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - tools: { - exec: { - allowSecurity: ["deny"], - allowHosts: ["auto"], - }, + const configPath = await writePolicyFixture({ + tools: { + exec: { + allowSecurity: ["deny"], + allowHosts: ["auto"], }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1431,7 +1273,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("keeps omitted auto-host exec security full when sandbox is non-main only", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), agents: { @@ -1440,19 +1281,14 @@ describe("registerPolicyDoctorChecks", () => { }, }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - tools: { - exec: { - allowSecurity: ["deny"], - allowHosts: ["auto"], - }, + const configPath = await writePolicyFixture({ + tools: { + exec: { + allowSecurity: ["deny"], + allowHosts: ["auto"], }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1478,7 +1314,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("reports gateway exposure settings denied by policy", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), gateway: { @@ -1507,36 +1342,31 @@ describe("registerPolicyDoctorChecks", () => { }, }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - exposure: { - allowNonLoopbackBind: false, - allowTailscaleFunnel: false, - }, - auth: { - requireAuth: true, - requireExplicitRateLimit: true, - }, - controlUi: { - allowInsecure: false, - }, - remote: { - allow: false, - }, - http: { - denyEndpoints: ["chatCompletions", "responses"], - requireUrlAllowlists: true, - }, - nodes: { - denyCommands: ["system.run"], - }, + const configPath = await writePolicyFixture({ + gateway: { + exposure: { + allowNonLoopbackBind: false, + allowTailscaleFunnel: false, }, - }), - "utf-8", - ); + auth: { + requireAuth: true, + requireExplicitRateLimit: true, + }, + controlUi: { + allowInsecure: false, + }, + remote: { + allow: false, + }, + http: { + denyEndpoints: ["chatCompletions", "responses"], + requireUrlAllowlists: true, + }, + nodes: { + denyCommands: ["system.run"], + }, + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1597,7 +1427,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("does not report gateway node commands denied by runtime config", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), gateway: { @@ -1609,18 +1438,13 @@ describe("registerPolicyDoctorChecks", () => { }, }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - nodes: { - denyCommands: ["system.run"], - }, + const configPath = await writePolicyFixture({ + gateway: { + nodes: { + denyCommands: ["system.run"], }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1629,25 +1453,19 @@ describe("registerPolicyDoctorChecks", () => { }); it("reports gateway node commands denied by policy without explicit extra allows", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), gateway: { nodes: {}, }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - nodes: { - denyCommands: ["system.run"], - }, + const configPath = await writePolicyFixture({ + gateway: { + nodes: { + denyCommands: ["system.run"], }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1663,23 +1481,17 @@ describe("registerPolicyDoctorChecks", () => { }); it("reports omitted gateway bind when non-loopback exposure is denied", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), gateway: {}, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - exposure: { - allowNonLoopbackBind: false, - }, + const configPath = await writePolicyFixture({ + gateway: { + exposure: { + allowNonLoopbackBind: false, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1695,25 +1507,19 @@ describe("registerPolicyDoctorChecks", () => { }); it("does not report omitted gateway bind when Tailscale forces loopback", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), gateway: { tailscale: { mode: "serve" }, }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - exposure: { - allowNonLoopbackBind: false, - }, + const configPath = await writePolicyFixture({ + gateway: { + exposure: { + allowNonLoopbackBind: false, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1722,25 +1528,19 @@ describe("registerPolicyDoctorChecks", () => { }); it("reports preserved Tailscale Funnel routes when policy denies Funnel exposure", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), gateway: { tailscale: { mode: "serve", preserveFunnel: true }, }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - exposure: { - allowTailscaleFunnel: false, - }, + const configPath = await writePolicyFixture({ + gateway: { + exposure: { + allowTailscaleFunnel: false, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1756,19 +1556,13 @@ describe("registerPolicyDoctorChecks", () => { }); it("reports missing gateway rate limits when gateway config is omitted", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - auth: { - requireExplicitRateLimit: true, - }, + const configPath = await writePolicyFixture({ + gateway: { + auth: { + requireExplicitRateLimit: true, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfgWithPolicy())); @@ -1784,7 +1578,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("does not report inactive custom bind hosts", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), gateway: { @@ -1792,18 +1585,13 @@ describe("registerPolicyDoctorChecks", () => { customBindHost: "0.0.0.0", }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - exposure: { - allowNonLoopbackBind: false, - }, + const configPath = await writePolicyFixture({ + gateway: { + exposure: { + allowNonLoopbackBind: false, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1812,7 +1600,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("does not report loopback custom bind hosts", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), gateway: { @@ -1820,18 +1607,13 @@ describe("registerPolicyDoctorChecks", () => { customBindHost: "127.0.0.1", }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - exposure: { - allowNonLoopbackBind: false, - }, + const configPath = await writePolicyFixture({ + gateway: { + exposure: { + allowNonLoopbackBind: false, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1840,7 +1622,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("reports valid non-loopback custom bind hosts", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), gateway: { @@ -1848,18 +1629,13 @@ describe("registerPolicyDoctorChecks", () => { customBindHost: "192.168.1.20", }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - exposure: { - allowNonLoopbackBind: false, - }, + const configPath = await writePolicyFixture({ + gateway: { + exposure: { + allowNonLoopbackBind: false, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1875,7 +1651,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("does not report blank custom bind config as active non-loopback exposure", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), gateway: { @@ -1883,18 +1658,13 @@ describe("registerPolicyDoctorChecks", () => { customBindHost: " ", }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - exposure: { - allowNonLoopbackBind: false, - }, + const configPath = await writePolicyFixture({ + gateway: { + exposure: { + allowNonLoopbackBind: false, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1905,7 +1675,6 @@ describe("registerPolicyDoctorChecks", () => { it.each(["localhost", "::1", "192.168.001.20"])( "does not report invalid custom bind host %s as active non-loopback exposure", async (customBindHost) => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), gateway: { @@ -1913,18 +1682,13 @@ describe("registerPolicyDoctorChecks", () => { customBindHost, }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - exposure: { - allowNonLoopbackBind: false, - }, + const configPath = await writePolicyFixture({ + gateway: { + exposure: { + allowNonLoopbackBind: false, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1934,7 +1698,6 @@ describe("registerPolicyDoctorChecks", () => { ); it("reports configured gateway remote URLs when remote mode is active", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), gateway: { @@ -1944,18 +1707,13 @@ describe("registerPolicyDoctorChecks", () => { }, }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - remote: { - allow: false, - }, + const configPath = await writePolicyFixture({ + gateway: { + remote: { + allow: false, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -1977,7 +1735,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("does not report inert remote config outside remote mode", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), gateway: { @@ -1986,18 +1743,13 @@ describe("registerPolicyDoctorChecks", () => { }, }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - remote: { - allow: false, - }, + const configPath = await writePolicyFixture({ + gateway: { + remote: { + allow: false, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -2006,7 +1758,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("reports default Responses URL fetching without allowlists", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), gateway: { @@ -2019,18 +1770,13 @@ describe("registerPolicyDoctorChecks", () => { }, }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - http: { - requireUrlAllowlists: true, - }, + const configPath = await writePolicyFixture({ + gateway: { + http: { + requireUrlAllowlists: true, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); @@ -2055,7 +1801,6 @@ describe("registerPolicyDoctorChecks", () => { }); it("reports wildcard Responses URL allowlists as unrestricted", async () => { - const configPath = join(workspaceDir, "openclaw.jsonc"); const cfg = { ...cfgWithPolicy(), gateway: { @@ -2070,18 +1815,13 @@ describe("registerPolicyDoctorChecks", () => { }, }, } as unknown as OpenClawConfig; - await fs.writeFile(configPath, "{}", "utf-8"); - await fs.writeFile( - join(workspaceDir, "policy.jsonc"), - JSON.stringify({ - gateway: { - http: { - requireUrlAllowlists: true, - }, + const configPath = await writePolicyFixture({ + gateway: { + http: { + requireUrlAllowlists: true, }, - }), - "utf-8", - ); + }, + }); registerPolicyDoctorChecks(); const result = await runDoctorLintChecks(ctx(configPath, cfg)); diff --git a/extensions/policy/src/doctor/register.test-harness.ts b/extensions/policy/src/doctor/register.test-harness.ts index c23973191993..fc059120f390 100644 --- a/extensions/policy/src/doctor/register.test-harness.ts +++ b/extensions/policy/src/doctor/register.test-harness.ts @@ -32,6 +32,20 @@ export function cfgWithPolicy(settings: Record = {}): OpenClawC }; } +export async function writePolicyFixture( + ...json: Parameters +): Promise { + const [policy] = json; + const configPath = join(workspaceDir, "openclaw.jsonc"); + await fs.writeFile(configPath, "{}", "utf-8"); + await fs.writeFile( + join(workspaceDir, "policy.jsonc"), + typeof policy === "string" ? policy : JSON.stringify(...json), + "utf-8", + ); + return configPath; +} + export function ctx(configPath: string, cfg: OpenClawConfig = {}): HealthCheckContext { return { mode: "lint", @@ -74,6 +88,13 @@ export async function runPolicyChecks(checkCtx: HealthCheckContext): Promise<{ return { findings }; } +export async function runPolicyChecksFixture( + policy: unknown, + cfg: OpenClawConfig = cfgWithPolicy(), +) { + return runPolicyChecks(ctx(await writePolicyFixture(policy), cfg)); +} + export async function runPolicyDoctorLint(checkCtx: HealthCheckContext) { return runDoctorLintChecks(checkCtx, { checks: registerChecks() }); } @@ -103,7 +124,7 @@ export async function runPolicyRepairCheck(checkId: string, repairCheckCtx: Heal return { ...result, findings, config, remainingFindings }; } -export const describe0BeforeEach0 = async () => { +export const setupPolicyDoctorTest = async () => { clearHealthChecksForTest(); originalOpenClawHome = process.env.OPENCLAW_HOME; originalOpenClawStateDir = process.env.OPENCLAW_STATE_DIR; @@ -125,7 +146,7 @@ export const describe0BeforeEach0 = async () => { } }; -export const describe0AfterEach1 = async () => { +export const teardownPolicyDoctorTest = async () => { if (originalOpenClawHome === undefined) { delete process.env.OPENCLAW_HOME; } else {