Files
openclaw/src/plugins/install-security-scan.runtime.test.ts

285 lines
9.0 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const runInstallPolicyMock = vi.fn();
const findBlockedManifestDependenciesMock = vi.fn();
const findBlockedNodeModulesDirectoryMock = vi.fn();
const findBlockedNodeModulesFileAliasMock = vi.fn();
const findBlockedPackageDirectoryInPathMock = vi.fn();
const findBlockedPackageFileAliasInPathMock = vi.fn();
const getGlobalHookRunnerMock = vi.fn();
vi.mock("../security/install-policy.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../security/install-policy.js")>();
return {
...actual,
runInstallPolicy: (...args: unknown[]) => runInstallPolicyMock(...args),
};
});
vi.mock("./dependency-denylist.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./dependency-denylist.js")>();
return {
...actual,
findBlockedManifestDependencies: (...args: unknown[]) =>
findBlockedManifestDependenciesMock(...args),
findBlockedNodeModulesDirectory: (...args: unknown[]) =>
findBlockedNodeModulesDirectoryMock(...args),
findBlockedNodeModulesFileAlias: (...args: unknown[]) =>
findBlockedNodeModulesFileAliasMock(...args),
findBlockedPackageDirectoryInPath: (...args: unknown[]) =>
findBlockedPackageDirectoryInPathMock(...args),
findBlockedPackageFileAliasInPath: (...args: unknown[]) =>
findBlockedPackageFileAliasInPathMock(...args),
};
});
vi.mock("./hook-runner-global.js", () => ({
getGlobalHookRunner: () => getGlobalHookRunnerMock(),
}));
const {
evaluateSkillInstallPolicyRuntime,
preflightPluginNpmInstallPolicyRuntime,
scanBundleInstallSourceRuntime,
scanFileInstallSourceRuntime,
} = await import("./install-security-scan.runtime.js");
function expectOnlyOperatorPolicyRan() {
expect(runInstallPolicyMock).toHaveBeenCalledTimes(1);
expect(findBlockedManifestDependenciesMock).not.toHaveBeenCalled();
expect(findBlockedNodeModulesDirectoryMock).not.toHaveBeenCalled();
expect(findBlockedNodeModulesFileAliasMock).not.toHaveBeenCalled();
expect(findBlockedPackageDirectoryInPathMock).not.toHaveBeenCalled();
expect(findBlockedPackageFileAliasInPathMock).not.toHaveBeenCalled();
expect(getGlobalHookRunnerMock).not.toHaveBeenCalled();
}
beforeEach(() => {
runInstallPolicyMock.mockReset();
findBlockedManifestDependenciesMock.mockReset();
findBlockedNodeModulesDirectoryMock.mockReset();
findBlockedNodeModulesFileAliasMock.mockReset();
findBlockedPackageDirectoryInPathMock.mockReset();
findBlockedPackageFileAliasInPathMock.mockReset();
getGlobalHookRunnerMock.mockReset();
});
describe("install security scan official bypass", () => {
it("bypasses plugin install friction for bundled OpenClaw sources", async () => {
const result = await scanBundleInstallSourceRuntime({
logger: {},
pluginId: "openclaw/kitchen-sink",
sourceDir: "/tmp/openclaw-bundled-plugin",
source: { kind: "bundled", authority: "openclaw", mutable: false, network: false },
});
expect(result).toBeUndefined();
expectOnlyOperatorPolicyRan();
});
it("bypasses plugin install friction for official ClawHub sources", async () => {
const result = await scanBundleInstallSourceRuntime({
logger: {},
pluginId: "@openclaw/matrix",
sourceDir: "/tmp/openclaw-official-clawhub-plugin",
source: { kind: "clawhub", authority: "official", mutable: false, network: true },
});
expect(result).toBeUndefined();
expectOnlyOperatorPolicyRan();
});
it("bypasses skill install friction for bundled OpenClaw sources", async () => {
const result = await evaluateSkillInstallPolicyRuntime({
installId: "node",
logger: {},
origin: {
type: "openclaw-bundled",
skillName: "peekaboo",
installId: "node",
},
source: { kind: "bundled", authority: "openclaw", mutable: false, network: false },
skillName: "peekaboo",
sourceDir: "/tmp/openclaw-bundled-skill/peekaboo",
});
expect(result).toBeUndefined();
expectOnlyOperatorPolicyRan();
});
it("runs only operator policy for official immutable npm sources", async () => {
const result = await preflightPluginNpmInstallPolicyRuntime({
logger: {},
packageName: "@openclaw/matrix",
requestedSpecifier: "@openclaw/matrix@latest",
source: { kind: "npm", authority: "official", mutable: false, network: true },
sourcePath: "/tmp/openclaw-official-npm",
sourcePathKind: "directory",
});
expect(result).toBeUndefined();
expectOnlyOperatorPolicyRan();
});
it("lets operator policy block official sources", async () => {
runInstallPolicyMock.mockResolvedValueOnce({
blocked: {
code: "security_scan_blocked",
reason: "blocked by operator policy",
},
});
const result = await scanBundleInstallSourceRuntime({
logger: {},
pluginId: "@openclaw/matrix",
sourceDir: "/tmp/openclaw-official-clawhub-plugin",
source: { kind: "clawhub", authority: "official", mutable: false, network: true },
});
expect(result).toEqual({
blocked: {
code: "security_scan_blocked",
reason: "blocked by operator policy",
},
});
expectOnlyOperatorPolicyRan();
});
it("still runs install policy for mutable workspace skill sources", async () => {
runInstallPolicyMock.mockResolvedValueOnce({
blocked: {
code: "security_scan_blocked",
reason: "blocked by operator policy",
},
});
const result = await evaluateSkillInstallPolicyRuntime({
installId: "node",
logger: {},
origin: {
type: "workspace",
skillName: "local-skill",
installId: "node",
},
source: { kind: "workspace", authority: "user", mutable: true, network: false },
skillName: "local-skill",
sourceDir: "/tmp/local-skill",
});
expect(result).toEqual({
blocked: {
code: "security_scan_blocked",
reason: "blocked by operator policy",
},
});
expect(runInstallPolicyMock).toHaveBeenCalledTimes(1);
});
});
describe("legacy file install scan compatibility", () => {
it("preserves policy and hook metadata for published lazy install chunks", async () => {
const warnings: string[] = [];
const hasHooks = vi.fn().mockReturnValue(true);
const runBeforeInstall = vi.fn().mockResolvedValue(undefined);
getGlobalHookRunnerMock.mockReturnValue({ hasHooks, runBeforeInstall });
runInstallPolicyMock.mockResolvedValueOnce({
findings: [
{
ruleId: "registry-review",
severity: "warn",
message: "Registry requires review.",
},
],
});
const result = await scanFileInstallSourceRuntime({
filePath: "/tmp/payload.js",
logger: { warn: (message) => warnings.push(message) },
mode: "update",
pluginId: "payload",
requestedSpecifier: "./payload.js",
});
expect(result).toBeUndefined();
expect(warnings).toEqual(["Install policy: Registry requires review."]);
expect(runInstallPolicyMock).toHaveBeenCalledWith({
config: undefined,
logger: expect.any(Object),
request: {
targetName: "payload",
targetType: "plugin",
sourcePath: "/tmp/payload.js",
sourcePathKind: "file",
source: { kind: "file", authority: "user", mutable: true, network: false },
origin: { type: "plugin-file" },
request: {
kind: "plugin-file",
mode: "update",
requestedSpecifier: "./payload.js",
},
plugin: {
contentType: "file",
pluginId: "payload",
extensions: ["payload.js"],
},
},
});
expect(hasHooks).toHaveBeenCalledWith("before_install");
expect(runBeforeInstall).toHaveBeenCalledWith(
{
targetName: "payload",
targetType: "plugin",
origin: "plugin-file",
sourcePath: "/tmp/payload.js",
sourcePathKind: "file",
request: {
kind: "plugin-file",
mode: "update",
requestedSpecifier: "./payload.js",
},
builtinScan: {
status: "ok",
scannedFiles: 0,
critical: 0,
warn: 0,
info: 0,
findings: [],
},
plugin: {
contentType: "file",
pluginId: "payload",
extensions: ["payload.js"],
},
},
{
origin: "plugin-file",
targetType: "plugin",
requestKind: "plugin-file",
},
);
});
it("returns operator policy blocks before invoking hooks", async () => {
runInstallPolicyMock.mockResolvedValueOnce({
blocked: {
code: "security_scan_blocked",
reason: "blocked by operator policy",
},
});
const result = await scanFileInstallSourceRuntime({
filePath: "/tmp/payload.js",
logger: {},
pluginId: "payload",
});
expect(result).toEqual({
blocked: {
code: "security_scan_blocked",
reason: "blocked by operator policy",
},
});
expect(getGlobalHookRunnerMock).not.toHaveBeenCalled();
});
});