refactor(exec-approvals): unify system.run binding and generate host env policy

This commit is contained in:
Peter Steinberger
2026-02-26 16:57:29 +01:00
parent baf1c8ea13
commit 4894d907fa
18 changed files with 858 additions and 342 deletions

View File

@@ -11,11 +11,23 @@ export type ExecHost = "sandbox" | "gateway" | "node";
export type ExecSecurity = "deny" | "allowlist" | "full";
export type ExecAsk = "off" | "on-miss" | "always";
export type SystemRunApprovalBindingV1 = {
version: 1;
argv: string[];
cwd: string | null;
agentId: string | null;
sessionKey: string | null;
envHash: string | null;
};
export type ExecApprovalRequestPayload = {
command: string;
commandArgv?: string[];
// Legacy env binding field (used for backward compatibility with old approvals).
envHash?: string | null;
// Optional UI-safe env key preview for approval prompts.
envKeys?: string[];
systemRunBindingV1?: SystemRunApprovalBindingV1 | null;
cwd?: string | null;
nodeId?: string | null;
host?: string | null;

View File

@@ -19,26 +19,44 @@ function parseSwiftStringArray(source: string, marker: string): string[] {
}
describe("host env security policy parity", () => {
it("keeps macOS HostEnvSanitizer lists in sync with shared JSON policy", () => {
it("keeps generated macOS host env policy in sync with shared JSON policy", () => {
const repoRoot = process.cwd();
const policyPath = path.join(repoRoot, "src/infra/host-env-security-policy.json");
const swiftPath = path.join(repoRoot, "apps/macos/Sources/OpenClaw/HostEnvSanitizer.swift");
const generatedSwiftPath = path.join(
repoRoot,
"apps/macos/Sources/OpenClaw/HostEnvSecurityPolicy.generated.swift",
);
const sanitizerSwiftPath = path.join(
repoRoot,
"apps/macos/Sources/OpenClaw/HostEnvSanitizer.swift",
);
const policy = JSON.parse(fs.readFileSync(policyPath, "utf8")) as HostEnvSecurityPolicy;
const swiftSource = fs.readFileSync(swiftPath, "utf8");
const generatedSource = fs.readFileSync(generatedSwiftPath, "utf8");
const sanitizerSource = fs.readFileSync(sanitizerSwiftPath, "utf8");
const swiftBlockedKeys = parseSwiftStringArray(swiftSource, "private static let blockedKeys");
const swiftBlockedKeys = parseSwiftStringArray(generatedSource, "static let blockedKeys");
const swiftBlockedOverrideKeys = parseSwiftStringArray(
swiftSource,
"private static let blockedOverrideKeys",
generatedSource,
"static let blockedOverrideKeys",
);
const swiftBlockedPrefixes = parseSwiftStringArray(
swiftSource,
"private static let blockedPrefixes",
generatedSource,
"static let blockedPrefixes",
);
expect(swiftBlockedKeys).toEqual(policy.blockedKeys);
expect(swiftBlockedOverrideKeys).toEqual(policy.blockedOverrideKeys ?? []);
expect(swiftBlockedPrefixes).toEqual(policy.blockedPrefixes);
expect(sanitizerSource).toContain(
"private static let blockedKeys = HostEnvSecurityPolicy.blockedKeys",
);
expect(sanitizerSource).toContain(
"private static let blockedOverrideKeys = HostEnvSecurityPolicy.blockedOverrideKeys",
);
expect(sanitizerSource).toContain(
"private static let blockedPrefixes = HostEnvSecurityPolicy.blockedPrefixes",
);
});
});