Files
openclaw/src/security/audit-node-command-findings.test.ts
2026-04-06 13:34:08 +01:00

135 lines
4.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import {
collectNodeDangerousAllowCommandFindings,
collectNodeDenyCommandPatternFindings,
} from "./audit-extra.sync.js";
function expectDetailText(params: {
detail: string | null | undefined;
name: string;
includes?: readonly string[];
excludes?: readonly string[];
}) {
for (const text of params.includes ?? []) {
expect(params.detail, `${params.name}:${text}`).toContain(text);
}
for (const text of params.excludes ?? []) {
expect(params.detail, `${params.name}:${text}`).not.toContain(text);
}
}
describe("security audit node command findings", () => {
it("evaluates ineffective gateway.nodes.denyCommands entries", () => {
const cases = [
{
name: "flags ineffective gateway.nodes.denyCommands entries",
cfg: {
gateway: {
nodes: {
denyCommands: ["system.*", "system.runx"],
},
},
} satisfies OpenClawConfig,
detailIncludes: ["system.*", "system.runx", "did you mean", "system.run"],
},
{
name: "suggests prefix-matching commands for unknown denyCommands entries",
cfg: {
gateway: {
nodes: {
denyCommands: ["system.run.prep"],
},
},
} satisfies OpenClawConfig,
detailIncludes: ["system.run.prep", "did you mean", "system.run.prepare"],
},
{
name: "keeps unknown denyCommands entries without suggestions when no close command exists",
cfg: {
gateway: {
nodes: {
denyCommands: ["zzzzzzzzzzzzzz"],
},
},
} satisfies OpenClawConfig,
detailIncludes: ["zzzzzzzzzzzzzz"],
detailExcludes: ["did you mean"],
},
] as const;
for (const testCase of cases) {
const findings = collectNodeDenyCommandPatternFindings(testCase.cfg);
const finding = findings.find(
(entry) => entry.checkId === "gateway.nodes.deny_commands_ineffective",
);
expect(finding?.severity, testCase.name).toBe("warn");
expectDetailText({
detail: finding?.detail,
name: testCase.name,
includes: testCase.detailIncludes,
excludes: "detailExcludes" in testCase ? testCase.detailExcludes : [],
});
}
});
it("evaluates dangerous gateway.nodes.allowCommands findings", () => {
const cases: Array<{
name: string;
cfg: OpenClawConfig;
expectedSeverity?: "warn" | "critical";
expectedAbsent?: boolean;
}> = [
{
name: "loopback gateway",
cfg: {
gateway: {
bind: "loopback",
nodes: { allowCommands: ["camera.snap", "screen.record"] },
},
} satisfies OpenClawConfig,
expectedSeverity: "warn" as const,
},
{
name: "lan-exposed gateway",
cfg: {
gateway: {
bind: "lan",
nodes: { allowCommands: ["camera.snap", "screen.record"] },
},
} satisfies OpenClawConfig,
expectedSeverity: "critical" as const,
},
{
name: "denied again suppresses dangerous allowCommands finding",
cfg: {
gateway: {
nodes: {
allowCommands: ["camera.snap", "screen.record"],
denyCommands: ["camera.snap", "screen.record"],
},
},
} satisfies OpenClawConfig,
expectedAbsent: true,
},
];
for (const testCase of cases) {
const findings = collectNodeDangerousAllowCommandFindings(testCase.cfg);
const finding = findings.find(
(entry) => entry.checkId === "gateway.nodes.allow_commands_dangerous",
);
if (testCase.expectedAbsent) {
expect(finding, testCase.name).toBeUndefined();
continue;
}
expect(finding?.severity, testCase.name).toBe(testCase.expectedSeverity);
expectDetailText({
detail: finding?.detail,
name: testCase.name,
includes: ["camera.snap", "screen.record"],
});
}
});
});