diff --git a/src/domain/processCapture.ts b/src/domain/processCapture.ts index ed8554b86..d6ae99d8d 100644 --- a/src/domain/processCapture.ts +++ b/src/domain/processCapture.ts @@ -42,7 +42,7 @@ export const processScenarioSchema = z environment: z.record(environmentName, z.string()).default({}), inherit_environment: z.array(environmentName).max(64).default([]), secret_aliases: z.array(environmentName).max(64).default([]), - network_access: z.literal("host").default("host"), + network_access: z.enum(["loopback", "host"]).default("loopback"), filesystem_roots: z.array(z.string().startsWith("/")).max(16).default([]), events: z .array( diff --git a/tests/processCapture.test.ts b/tests/processCapture.test.ts index b00106b38..2f3edbae9 100644 --- a/tests/processCapture.test.ts +++ b/tests/processCapture.test.ts @@ -72,8 +72,12 @@ describe("process capture domain", () => { }); it("requires explicit operator approval for host network access", () => { + const scenario = parseProcessScenario({ + ...base, + network_access: "host", + }); expect( - authorizeProcessScenario(parseProcessScenario(base), { + authorizeProcessScenario(scenario, { enabled: true, executableRoots: ["/bin"], workingRoots: ["/tmp"], @@ -86,6 +90,20 @@ describe("process capture domain", () => { }); }); + it("allows loopback replay without external network approval", () => { + const scenario = parseProcessScenario(base); + expect(scenario.network_access).toBe("loopback"); + expect( + authorizeProcessScenario(scenario, { + enabled: true, + executableRoots: ["/bin"], + workingRoots: ["/tmp"], + allowedEnvironment: [], + allowExternalNetwork: false, + }), + ).toEqual({ allowed: true }); + }); + it("refuses paths and environment outside operator policy", () => { const scenario = parseProcessScenario({ ...base,